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

View File

@@ -17,6 +17,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
@@ -74,19 +75,19 @@ public class GeyserMappings {
return mappings.size(); 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(); String displayName = stack.getHoverName().getString();
int protectionValue = 0; // TODO check the attributes int protectionValue = 0; // TODO check the attributes
GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter) GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter,
.forEach(mapping -> { (mapping, texture) -> {
try { try {
map(stack.getItemHolder(), mapping); map(stack.getItemHolder(), mapping);
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
reporter.forChild(() -> "mapping with bedrock identifier " + mapping.bedrockIdentifier() + " ").report(() -> "failed to add mapping to mappings file: " + exception.getMessage()); reporter.forChild(() -> "mapping with bedrock identifier " + mapping.bedrockIdentifier() + " ").report(() -> "failed to add mapping to mappings file: " + exception.getMessage());
return; 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); Object resolved = original.call(instance);
try { try {
// Couldn't be bothered setting up access wideners, this resolves the second component of the ResolvedModels record, which is called "models" // 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); ((ModelManagerMixin) (Object) Minecraft.getInstance().getModelManager()).unbakedResolvedModels = (Map<ResourceLocation, ResolvedModel>) resolved.getClass().getRecordComponents()[1].getAccessor().invoke(resolved);
} catch (IllegalAccessException | InvocationTargetException | ClassCastException exception) { } catch (IllegalAccessException | InvocationTargetException | ClassCastException exception) {
throw new RuntimeException(exception); throw new RuntimeException(exception);

View File

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