11package re .imc .geysermodelengineextension .managers .resourcepack .generator ;
22
33import com .google .gson .JsonArray ;
4+ import com .google .gson .JsonElement ;
45import com .google .gson .JsonObject ;
6+ import re .imc .geysermodelengineextension .GeyserModelEngineExtension ;
57import re .imc .geysermodelengineextension .managers .resourcepack .generator .data .BoneData ;
8+ import re .imc .geysermodelengineextension .managers .resourcepack .generator .data .TextureData ;
69import re .imc .geysermodelengineextension .util .ShortHashUtil ;
710
11+ import javax .imageio .ImageIO ;
12+ import java .awt .image .BufferedImage ;
13+ import java .io .ByteArrayInputStream ;
14+ import java .io .IOException ;
815import java .util .*;
916
1017public class RenderController {
@@ -14,15 +21,24 @@ public class RenderController {
1421 private final String modelId ;
1522 private final Map <String , BoneData > bones ;
1623 private final Entity entity ;
24+ private final Geometry geometry ;
25+ private final Map <String , BufferedImage > imageCache = new HashMap <>();
1726
18- public RenderController (String modelId , Map < String , BoneData > bones , Entity entity ) {
27+ public RenderController (String modelId , Geometry geometry , Entity entity ) {
1928 this .modelId = modelId ;
20- this .bones = bones ;
29+ this .geometry = geometry ;
30+ this .bones = geometry .getBones ();
2131 this .entity = entity ;
2232 }
2333
2434 // look, I'm fine with your other code and stuff, but I ain't using templates for JSON lmao
2535 public String generate (String namespace , boolean hashEnabled ) {
36+ boolean translucency = GeyserModelEngineExtension .getExtension ().getConfigManager ()
37+ .getConfig ().getBoolean ("options.resource-pack.translucent-materials" , true );
38+ double translucentThreshold = GeyserModelEngineExtension .getExtension ().getConfigManager ()
39+ .getConfig ().getDouble ("options.resource-pack.translucent-threshold" );
40+ if (translucentThreshold <= 0 ) translucentThreshold = 0.5 ;
41+
2642 List <String > se = new ArrayList <>(bones .keySet ());
2743 Collections .sort (se );
2844 JsonObject root = new JsonObject ();
@@ -74,6 +90,7 @@ public String generate(String namespace, boolean hashEnabled) {
7490 String material = entity .getModelConfig ().getTextureMaterials ().get (key );
7591
7692 JsonObject materialItem = new JsonObject ();
93+ boolean defaultMaterial = false ;
7794 if (material != null ) {
7895 materialItem .addProperty ("*" , "Material." + material );
7996 } else if (anim != null ) {
@@ -90,10 +107,25 @@ public String generate(String namespace, boolean hashEnabled) {
90107 scale .add (1.0 );
91108 scale .add ("1 / " + anim .frames );
92109 uvAnim .add ("scale" , scale );
110+ } else if (translucency && semiFraction (key ) >= 0.999 ) {
111+ materialItem .addProperty ("*" , "Material.blend" );
93112 } else {
94113 materialItem .addProperty ("*" , "Material.default" );
114+ defaultMaterial = true ;
95115 }
96116 materials .add (materialItem );
117+ if (translucency && defaultMaterial ) {
118+ for (String tBone : translucentBones (key , uvBonesId , translucentThreshold )) {
119+ JsonObject boneMaterial = new JsonObject ();
120+ boneMaterial .addProperty (tBone , "Material.blend" );
121+ materials .add (boneMaterial );
122+ }
123+ for (String [] split : entity .getTranslucentSplits ().getOrDefault (key , Collections .emptyList ())) {
124+ JsonObject boneMaterial = new JsonObject ();
125+ boneMaterial .addProperty (split [0 ], "Material.blend" );
126+ materials .add (boneMaterial );
127+ }
128+ }
97129 controller .add ("materials" , materials );
98130
99131 JsonArray textures = new JsonArray ();
@@ -111,6 +143,7 @@ public String generate(String namespace, boolean hashEnabled) {
111143 visibilityDefault .addProperty ("*" , false );
112144 partVisibility .add (visibilityDefault );
113145 int i = 0 ;
146+ Map <String , JsonElement > capturedVisibility = new HashMap <>();
114147 List <String > sorted = new ArrayList <>(bones .keySet ());
115148 Map <String , String > originalId = new HashMap <>();
116149 ListIterator <String > iterator = sorted .listIterator ();
@@ -172,6 +205,7 @@ public String generate(String namespace, boolean hashEnabled) {
172205 visibilityItem .addProperty (boneName , "math.mod(math.floor(query.property('" + namespace + ":bone" + index / 24 + "') / " + n + "), 2) == 1" );
173206 }
174207 partVisibility .add (visibilityItem );
208+ capturedVisibility .put (boneName , visibilityItem .get (boneName ));
175209 if (!uvBonesId .contains ("*" )) {
176210 processedBones .add (bone );
177211 }
@@ -180,11 +214,82 @@ public String generate(String namespace, boolean hashEnabled) {
180214 i ++;
181215 }
182216 }
217+ for (String [] split : entity .getTranslucentSplits ().getOrDefault (key , Collections .emptyList ())) {
218+ JsonElement vis = capturedVisibility .get (split [1 ]);
219+ if (vis != null ) {
220+ JsonObject splitVisibility = new JsonObject ();
221+ splitVisibility .add (split [0 ], vis .deepCopy ());
222+ partVisibility .add (splitVisibility );
223+ }
224+ }
183225 controller .add ("part_visibility" , partVisibility );
184226 //}
185227 }
186228
187229 return root .toString ();
188230 }
189231
232+ private BufferedImage image (String key ) {
233+ if (imageCache .containsKey (key )) return imageCache .get (key );
234+ BufferedImage img = null ;
235+ TextureData texture = entity .getTextureMap ().get (key );
236+ if (texture != null && texture .getImage () != null ) {
237+ try {
238+ img = ImageIO .read (new ByteArrayInputStream (texture .getImage ()));
239+ if (img != null && !img .getColorModel ().hasAlpha ()) img = null ;
240+ } catch (IOException ignored ) {
241+ }
242+ }
243+ imageCache .put (key , img );
244+ return img ;
245+ }
246+
247+ private double semiFraction (String key ) {
248+ BufferedImage img = image (key );
249+ if (img == null ) return 0 ;
250+ int w = img .getWidth (), h = img .getHeight ();
251+ long semi = 0 , visible = 0 ;
252+ for (int y = 0 ; y < h ; y ++) {
253+ for (int x = 0 ; x < w ; x ++) {
254+ int a = (img .getRGB (x , y ) >>> 24 ) & 0xFF ;
255+ if (a == 0 ) continue ;
256+ visible ++;
257+ if (a < 255 ) semi ++;
258+ }
259+ }
260+ return visible == 0 ? 0 : (double ) semi / visible ;
261+ }
262+
263+ private Set <String > translucentBones (String key , Set <String > uvBonesId , double threshold ) {
264+ BufferedImage img = image (key );
265+ if (img == null ) return Collections .emptySet ();
266+
267+ JsonObject description = geometry .getInternal ().get ("description" ).getAsJsonObject ();
268+ Integer [] uvSize = entity .getModelConfig ().getPerTextureUvSize ().get (key );
269+ double [] sc = TranslucencySplitter .scale (description , uvSize , img );
270+ double sx = sc [0 ], sy = sc [1 ];
271+
272+ boolean allBones = uvBonesId .contains ("*" );
273+ Set <String > bound = new HashSet <>();
274+ for (String b : uvBonesId ) bound .add (b .toLowerCase ());
275+
276+ Set <String > result = new LinkedHashSet <>();
277+ for (JsonElement element : geometry .getInternal ().get ("bones" ).getAsJsonArray ()) {
278+ JsonObject bone = element .getAsJsonObject ();
279+ String name = bone .get ("name" ).getAsString ();
280+ if (name .endsWith ("_t" )) continue ;
281+ if (!allBones && !bound .contains (name .toLowerCase ())) continue ;
282+ if (!bone .has ("cubes" )) continue ;
283+
284+ boolean anyGlass = false , anyOpaque = false ;
285+ for (JsonElement cube : bone .get ("cubes" ).getAsJsonArray ()) {
286+ int type = TranslucencySplitter .cubeClass (cube .getAsJsonObject (), img , sx , sy , threshold );
287+ if (type == TranslucencySplitter .GLASS ) anyGlass = true ;
288+ else if (type == TranslucencySplitter .OPAQUE ) anyOpaque = true ;
289+ }
290+ if (anyGlass && !anyOpaque ) result .add (name );
291+ }
292+ return result ;
293+ }
294+
190295}
0 commit comments