1+ package org .embeddedt .modernfix .neoforge .mixin .perf .dynamic_resources .ctm ;
2+
3+ import com .google .common .collect .Multimap ;
4+ import com .google .common .collect .Sets ;
5+ import com .mojang .datafixers .util .Pair ;
6+ import net .minecraft .client .resources .model .*;
7+ import net .minecraft .resources .ResourceLocation ;
8+ import org .embeddedt .modernfix .ModernFixClient ;
9+ import org .embeddedt .modernfix .annotation .ClientOnlyMixin ;
10+ import org .embeddedt .modernfix .annotation .RequiresMod ;
11+ import org .embeddedt .modernfix .api .entrypoint .ModernFixClientIntegration ;
12+ import org .spongepowered .asm .mixin .Final ;
13+ import org .spongepowered .asm .mixin .Mixin ;
14+ import org .spongepowered .asm .mixin .Shadow ;
15+ import org .spongepowered .asm .mixin .injection .At ;
16+ import org .spongepowered .asm .mixin .injection .Inject ;
17+ import org .spongepowered .asm .mixin .injection .callback .CallbackInfo ;
18+ import team .chisel .ctm .CTM ;
19+ import team .chisel .ctm .api .model .IModelCTM ;
20+ import team .chisel .ctm .client .model .AbstractCTMBakedModel ;
21+ import team .chisel .ctm .client .model .ModelCTM ;
22+ import team .chisel .ctm .client .texture .IMetadataSectionCTM ;
23+ import team .chisel .ctm .client .util .ResourceUtil ;
24+ import team .chisel .ctm .client .util .TextureMetadataHandler ;
25+
26+ import javax .annotation .Nonnull ;
27+ import java .io .IOException ;
28+ import java .util .*;
29+
30+ @ Mixin (TextureMetadataHandler .class )
31+ @ RequiresMod ("ctm" )
32+ @ ClientOnlyMixin
33+ public abstract class TextureMetadataHandlerMixin implements ModernFixClientIntegration {
34+
35+ @ Shadow (remap = false ) @ Nonnull protected abstract BakedModel wrap (UnbakedModel model , BakedModel object ) throws IOException ;
36+
37+ @ Shadow (remap = false ) @ Final private Multimap <ResourceLocation , Material > scrapedTextures ;
38+
39+ @ Inject (method = "<init>" , at = @ At ("RETURN" ))
40+ private void subscribeDynamic (CallbackInfo ci ) {
41+ ModernFixClient .CLIENT_INTEGRATIONS .add (this );
42+ }
43+
44+ @ Inject (method = { "onModelBake(Lnet/neoforged/neoforge/client/event/ModelEvent$BakingCompleted;)V" , "onModelBake(Lnet/neoforged/neoforge/client/event/ModelEvent$ModifyBakingResult;)V" }, at = @ At ("HEAD" ), cancellable = true , remap = false )
45+ private void noIteration (CallbackInfo ci ) {
46+ ci .cancel ();
47+ }
48+
49+ @ Override
50+ public BakedModel onBakedModelLoad (ModelResourceLocation mrl , UnbakedModel rootModel , BakedModel baked , ModelState state , ModelBakery bakery , ModelBakery .TextureGetter getter ) {
51+ if (!(baked instanceof AbstractCTMBakedModel ) && !baked .isCustomRenderer ()) {
52+ Deque <ResourceLocation > dependencies = new ArrayDeque <>();
53+ Set <ResourceLocation > seenModels = new HashSet <>();
54+ dependencies .push (mrl .id ());
55+ seenModels .add (mrl .id ());
56+ boolean shouldWrap = false ;
57+ Set <Pair <String , String >> errors = new HashSet <>();
58+ // Breadth-first loop through dependencies, exiting as soon as a CTM texture is found, and skipping duplicates/cycles
59+ while (!shouldWrap && !dependencies .isEmpty ()) {
60+ ResourceLocation dep = dependencies .pop ();
61+ UnbakedModel model ;
62+ try {
63+ model = dep == mrl .id () ? rootModel : bakery .getModel (dep );
64+ } catch (Exception e ) {
65+ continue ;
66+ }
67+
68+ Collection <Material > textures = Sets .newHashSet (scrapedTextures .get (dep ));
69+ Collection <ResourceLocation > newDependencies = model .getDependencies ();
70+ for (Material tex : textures ) {
71+ IMetadataSectionCTM meta = null ;
72+ // Cache all dependent texture metadata
73+ try {
74+ meta = ResourceUtil .getMetadata (ResourceUtil .spriteToAbsolute (tex .texture ())).orElse (null ); // TODO, lazy
75+ } catch (IOException e ) {} // Fallthrough
76+ if (meta != null ) {
77+ // At least one texture has CTM metadata, so we should wrap this model
78+ shouldWrap = true ;
79+ }
80+ }
81+
82+ for (ResourceLocation newDep : newDependencies ) {
83+ if (seenModels .add (newDep )) {
84+ dependencies .push (newDep );
85+ }
86+ }
87+ }
88+ if (shouldWrap ) {
89+ try {
90+ baked = wrap (rootModel , baked );
91+ handleInit (mrl , baked , bakery , getter );
92+ dependencies .clear ();
93+ } catch (IOException e ) {
94+ CTM .logger .error ("Could not wrap model " + mrl + ". Aborting..." , e );
95+ }
96+ }
97+ }
98+ return baked ;
99+ }
100+
101+ private void handleInit (ModelResourceLocation key , BakedModel wrappedModel , ModelBakery bakery , ModelBakery .TextureGetter spriteGetter ) {
102+ if (wrappedModel instanceof AbstractCTMBakedModel baked ) {
103+ IModelCTM var10 = baked .getModel ();
104+ if (var10 instanceof ModelCTM ctmModel ) {
105+ if (!ctmModel .isInitialized ()) {
106+ // Clear the baked cache as upstream CTM does
107+ ((CTMModelBakeryAccessor )bakery ).mfix$getBakedCache ().clear ();
108+ ModelBakery .ModelBakerImpl baker = bakery .new ModelBakerImpl (spriteGetter , key );
109+ ctmModel .bake (baker , Material ::sprite , BlockModelRotation .X0_Y0 );
110+ }
111+ }
112+ }
113+ }
114+ }
0 commit comments