1
0
mirror of https://github.com/GeyserMC/Rainbow.git synced 2025-12-19 14:59:16 +00:00

Proper handheld detection and model -> texture

This commit is contained in:
Eclipse
2025-07-04 12:03:07 +00:00
parent fce70853e0
commit dcce9d3722
4 changed files with 55 additions and 54 deletions

View File

@@ -16,6 +16,8 @@ import net.minecraft.client.renderer.item.properties.select.Charge;
import net.minecraft.client.renderer.item.properties.select.ContextDimension;
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty;
import net.minecraft.client.renderer.item.properties.select.TrimMaterialProperty;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
@@ -34,49 +36,56 @@ import org.geysermc.packgenerator.mixin.SelectItemModelAccessor;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Stream;
public class GeyserItemMapper {
private static final List<ResourceLocation> HANDHELD_MODELS = Stream.of("item/handheld", "item/handheld_rod", "item/handheld_mace")
.map(ResourceLocation::withDefaultNamespace)
.toList();
public static Stream<GeyserSingleDefinition> mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch,
ProblemReporter reporter) {
public static void mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
BiConsumer<GeyserSingleDefinition, ResourceLocation> mappingTextureConsumer) {
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch, reporter.forChild(() -> "client item definition " + modelLocation + " "));
return mapItem(model, context);
MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch, reporter.forChild(() -> "client item definition " + modelLocation + " "), mappingTextureConsumer);
mapItem(model, context);
}
private static Stream<GeyserSingleDefinition> mapItem(ItemModel model, MappingContext context) {
private static void mapItem(ItemModel model, MappingContext context) {
switch (model) {
case BlockModelWrapper modelWrapper -> {
ResourceLocation itemModelLocation = ((BlockModelWrapperLocationAccessor) modelWrapper).geyser_mappings_generator$getModelOrigin();
return ((ResolvedModelAccessor) Minecraft.getInstance().getModelManager()).geyser_mappings_generator$getResolvedModel(itemModelLocation)
.map(itemModel -> {
((ResolvedModelAccessor) Minecraft.getInstance().getModelManager()).geyser_mappings_generator$getResolvedModel(itemModelLocation)
.ifPresentOrElse(itemModel -> {
ResolvedModel parentModel = itemModel.parent();
boolean handheld = false;
if (parentModel != null) {
// debugName() returns the resource location of the model as a string
handheld = HANDHELD_MODELS.contains(ResourceLocation.parse(parentModel.debugName()));
}
ResourceLocation bedrockIdentifier = itemModelLocation;
if (bedrockIdentifier.getNamespace().equals(ResourceLocation.DEFAULT_NAMESPACE)) {
bedrockIdentifier = ResourceLocation.fromNamespaceAndPath("geyser_mc", itemModelLocation.getPath());
}
return Stream.of(context.create(bedrockIdentifier));
})
.orElseGet(() -> {
context.reporter.report(() -> "missing block model " + itemModelLocation);
return Stream.empty();
});
ResourceLocation texture = itemModelLocation;
Material layer0Texture = itemModel.getTopTextureSlots().getMaterial("layer0");
if (layer0Texture != null) {
texture = layer0Texture.texture();
}
case ConditionalItemModel conditional -> {
return mapConditionalModel(conditional, context.child("condition " + conditional + " "));
context.create(bedrockIdentifier, texture, handheld);
}, () -> context.reporter.report(() -> "missing block model " + itemModelLocation));
}
case SelectItemModel<?> select -> {
return mapSelectModel(select, context.child("select " + select + " "));
case ConditionalItemModel conditional -> mapConditionalModel(conditional, context.child("condition " + conditional + " "));
case SelectItemModel<?> select -> mapSelectModel(select, context.child("select " + select + " "));
default -> context.reporter.report(() -> "unable to map item model " + model.getClass());
}
default -> {}
}
context.reporter.report(() -> "unable to map item model " + model.getClass());
return Stream.empty();
}
private static Stream<GeyserSingleDefinition> mapConditionalModel(ConditionalItemModel model, MappingContext context) {
private static void mapConditionalModel(ConditionalItemModel model, MappingContext context) {
ItemModelPropertyTest property = ((ConditionalItemModelAccessor) model).getProperty();
GeyserConditionPredicate.Property predicateProperty = switch (property) {
case Broken ignored -> GeyserConditionPredicate.BROKEN;
@@ -88,19 +97,17 @@ public class GeyserItemMapper {
};
if (predicateProperty == null) {
context.reporter.report(() -> "unsupported conditional model property " + property);
return Stream.empty();
return;
}
ItemModel onTrue = ((ConditionalItemModelAccessor) model).getOnTrue();
ItemModel onFalse = ((ConditionalItemModelAccessor) model).getOnFalse();
return Stream.concat(
mapItem(onTrue, context.with(new GeyserConditionPredicate(predicateProperty, true), "condition on true ")),
mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false), "condition on false "))
);
mapItem(onTrue, context.with(new GeyserConditionPredicate(predicateProperty, true), "condition on true "));
mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false), "condition on false "));
}
private static <T> Stream<GeyserSingleDefinition> mapSelectModel(SelectItemModel<T> model, MappingContext context) {
private static <T> void mapSelectModel(SelectItemModel<T> model, MappingContext context) {
//noinspection unchecked
SelectItemModelProperty<T> property = ((SelectItemModelAccessor<T>) model).getProperty();
Function<T, GeyserMatchPredicate.MatchPredicateData> dataConstructor = switch (property) {
@@ -113,34 +120,31 @@ public class GeyserItemMapper {
};
if (dataConstructor == null) {
context.reporter.report(() -> "unsupported select model property " + property);
return Stream.empty();
return;
}
//noinspection unchecked
Object2ObjectMap<T, ItemModel> cases = ((SelectItemModelCasesAccessor<T>) model).geyser_mappings_generator$getCases();
return Stream.concat(
cases.entrySet().stream()
.flatMap(caze -> mapItem(caze.getValue(), context.with(new GeyserMatchPredicate(dataConstructor.apply(caze.getKey())), "select case " + caze.getKey() + " "))),
mapItem(cases.defaultReturnValue(), context.child("default case "))
);
cases.entrySet().forEach(caze -> mapItem(caze.getValue(), context.with(new GeyserMatchPredicate(dataConstructor.apply(caze.getKey())), "select case " + caze.getKey() + " ")));
mapItem(cases.defaultReturnValue(), context.child("default case "));
}
private record MappingContext(List<GeyserPredicate> predicateStack, ResourceLocation model, String displayName, int protectionValue, DataComponentPatch componentPatch,
ProblemReporter reporter) {
private record MappingContext(List<GeyserPredicate> predicateStack, ResourceLocation model, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
BiConsumer<GeyserSingleDefinition, ResourceLocation> mappingTextureConsumer) {
public MappingContext with(GeyserPredicate predicate, String childName) {
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), model, displayName, protectionValue, componentPatch,
reporter.forChild(() -> childName));
reporter.forChild(() -> childName), mappingTextureConsumer);
}
public MappingContext child(String childName) {
return new MappingContext(predicateStack, model, displayName, protectionValue, componentPatch, reporter.forChild(() -> childName));
return new MappingContext(predicateStack, model, displayName, protectionValue, componentPatch, reporter.forChild(() -> childName), mappingTextureConsumer);
}
public GeyserSingleDefinition create(ResourceLocation bedrockIdentifier) {
return new GeyserSingleDefinition(Optional.of(model), bedrockIdentifier, Optional.of(displayName), predicateStack,
new GeyserSingleDefinition.BedrockOptions(Optional.empty(), true, false, protectionValue), // TODO handheld prediction
componentPatch);
public void create(ResourceLocation bedrockIdentifier, ResourceLocation texture, boolean displayHandheld) {
mappingTextureConsumer.accept(new GeyserSingleDefinition(Optional.of(model), bedrockIdentifier, Optional.of(displayName), predicateStack,
new GeyserSingleDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, protectionValue), componentPatch), texture);
}
}
}

View File

@@ -17,6 +17,7 @@ import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -74,19 +75,19 @@ public class GeyserMappings {
return mappings.size();
}
public void map(ItemStack stack, ResourceLocation model, ProblemReporter reporter, Consumer<GeyserSingleDefinition> mappingConsumer) {
public void map(ItemStack stack, ResourceLocation model, ProblemReporter reporter, BiConsumer<GeyserSingleDefinition, ResourceLocation> mappingTextureConsumer) {
String displayName = stack.getHoverName().getString();
int protectionValue = 0; // TODO check the attributes
GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter)
.forEach(mapping -> {
GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter,
(mapping, texture) -> {
try {
map(stack.getItemHolder(), mapping);
} catch (IllegalArgumentException exception) {
reporter.forChild(() -> "mapping with bedrock identifier " + mapping.bedrockIdentifier() + " ").report(() -> "failed to add mapping to mappings file: " + exception.getMessage());
return;
}
mappingConsumer.accept(mapping);
mappingTextureConsumer.accept(mapping, texture);
});
}

View File

@@ -27,6 +27,7 @@ public abstract class ModelManagerMixin implements PreparableReloadListener, Aut
Object resolved = original.call(instance);
try {
// Couldn't be bothered setting up access wideners, this resolves the second component of the ResolvedModels record, which is called "models"
// Ideally we'd somehow use the "this" instance, but that's not possible here since the lambda we inject into is a static one
((ModelManagerMixin) (Object) Minecraft.getInstance().getModelManager()).unbakedResolvedModels = (Map<ResourceLocation, ResolvedModel>) resolved.getClass().getRecordComponents()[1].getAccessor().invoke(resolved);
} catch (IllegalAccessException | InvocationTargetException | ClassCastException exception) {
throw new RuntimeException(exception);

View File

@@ -101,13 +101,8 @@ public class BedrockPack {
}
};
mappings.map(stack, model, mapReporter, mapping -> {
// TODO a proper way to get texture from item model
itemTextures.withItemTexture(mapping, mapping.bedrockIdentifier().getPath());
ResourceLocation texture = mapping.bedrockIdentifier();
if (texture.getNamespace().equals("geyser_mc")) {
texture = ResourceLocation.withDefaultNamespace(texture.getPath());
}
mappings.map(stack, model, mapReporter, (mapping, texture) -> {
itemTextures.withItemTexture(mapping, texture.getPath());
texturesToExport.add(texture);
AttachableMapper.mapItem(stack, mapping.bedrockIdentifier(), texturesToExport::add).ifPresent(attachables::add);
});