11/*
2- * Copyright (c) 2009-2021 jMonkeyEngine
2+ * Copyright (c) 2009-2025 jMonkeyEngine
33 * All rights reserved.
44 *
55 * Redistribution and use in source and binary forms, with or without
3232package jme3test .stress ;
3333
3434import com .jme3 .app .SimpleApplication ;
35+ import com .jme3 .bounding .BoundingBox ;
36+ import com .jme3 .font .BitmapText ;
37+ import com .jme3 .input .KeyInput ;
38+ import com .jme3 .input .controls .ActionListener ;
39+ import com .jme3 .input .controls .KeyTrigger ;
40+ import com .jme3 .input .controls .Trigger ;
3541import com .jme3 .light .DirectionalLight ;
3642import com .jme3 .material .Material ;
37- import com .jme3 .math .Quaternion ;
43+ import com .jme3 .material .RenderState ;
44+ import com .jme3 .math .ColorRGBA ;
45+ import com .jme3 .math .FastMath ;
3846import com .jme3 .math .Vector3f ;
3947import com .jme3 .scene .Geometry ;
4048import com .jme3 .scene .Node ;
49+ import com .jme3 .scene .SceneGraphVisitorAdapter ;
4150import com .jme3 .scene .control .LodControl ;
51+ import com .jme3 .scene .control .UpdateControl ;
52+ import com .jme3 .scene .debug .WireBox ;
4253import jme3tools .optimize .GeometryBatchFactory ;
4354
44- public class TestBatchLod extends SimpleApplication {
55+ import java .util .Locale ;
56+
57+ public class TestBatchLod extends SimpleApplication implements ActionListener {
4558
4659 public static void main (String [] args ) {
4760 TestBatchLod app = new TestBatchLod ();
61+ app .setPauseOnLostFocus (false );
4862 app .start ();
4963 }
5064
65+ private BitmapText hud ;
66+ private Material lightMaterial ;
67+ private final Node debugNode = new Node ("DebugNode" );
68+ private LodControl lodControl ;
69+ // Tuning parameters
70+ private float trisPerPixel = 1.0f ;
71+ private float distTolerance = 1.0f ;
72+
5173 @ Override
5274 public void simpleInitApp () {
53- // inputManager.registerKeyBinding("USELOD", KeyInput.KEY_L);
75+
76+ configureCamera ();
77+
78+ hud = createLabelText (10 , 20 , "<placeholder>" );
79+ updateHud ();
5480
5581 DirectionalLight dl = new DirectionalLight ();
5682 dl .setDirection (new Vector3f (-1 , -1 , -1 ).normalizeLocal ());
@@ -59,30 +85,126 @@ public void simpleInitApp() {
5985 Node teapotNode = (Node ) assetManager .loadModel ("Models/Teapot/Teapot.mesh.xml" );
6086 Geometry teapot = (Geometry ) teapotNode .getChild (0 );
6187
62- Material mat = new Material (assetManager , "Common/MatDefs/Light/Lighting.j3md" );
63- mat .setFloat ("Shininess" , 16f );
64- mat .setBoolean ("VertexLighting" , true );
65- teapot .setMaterial (mat );
88+ lightMaterial = new Material (assetManager , "Common/MatDefs/Light/Lighting.j3md" );
89+ lightMaterial .setFloat ("Shininess" , 16f );
90+ lightMaterial .setBoolean ("VertexLighting" , true );
91+ lightMaterial .getAdditionalRenderState ().setWireframe (true );
92+ teapot .setMaterial (lightMaterial );
6693
67- // A special Material to visualize mesh normals:
68- //Material mat = new Material(assetManager, "Common/MatDefs/Misc/ShowNormals.j3md");
69- flyCam .setMoveSpeed (5 );
94+ boolean cloneMaterial = false ;
7095 for (int y = -5 ; y < 5 ; y ++) {
7196 for (int x = -5 ; x < 5 ; x ++) {
72- Geometry clonePot = teapot .clone ();
97+ Geometry geo = teapot .clone (cloneMaterial );
98+ geo .setLocalTranslation (x * .5f , 0 , y * .5f );
99+ geo .setLocalScale (.15f );
100+ geo .setMaterial (lightMaterial );
101+ debugNode .attachChild (geo );
102+ }
103+ }
104+
105+ lodControl = new LodControl ();
106+ boolean useLods = true ;
107+ Node batchNode = GeometryBatchFactory .optimize (debugNode , useLods );
108+ batchNode .depthFirstTraversal (new SceneGraphVisitorAdapter () {
109+ @ Override
110+ public void visit (Geometry geom ) {
111+ if (geom .getMesh ().getNumLodLevels () > 0 ) {
112+
113+ geom .addControl (lodControl );
114+ debugWorldBound ((BoundingBox ) geom .getWorldBound ());
73115
74- //clonePot.setMaterial(mat);
75- clonePot .setLocalTranslation (x * .5f , 0 , y * .5f );
76- clonePot .setLocalScale (.15f );
77- clonePot .setMaterial (mat );
78- rootNode .attachChild (clonePot );
116+ geom .addControl (new UpdateControl () {
117+ int lastLevel = -1 ;
118+ final BitmapText label = createLabelText (10 , 50 , "Updating..." );
119+
120+ @ Override
121+ protected void controlUpdate (float tpf ) {
122+ if (lastLevel != geom .getLodLevel ()) {
123+ lastLevel = geom .getLodLevel ();
124+ label .setText ("LodLevel: " + lastLevel );
125+ }
126+ }
127+ });
128+ }
129+ }
130+
131+ private void debugWorldBound (BoundingBox bbox ) {
132+ Geometry geo = WireBox .makeGeometry (bbox );
133+ Material mat = new Material (assetManager , "Common/MatDefs/Misc/Unshaded.j3md" );
134+ mat .setColor ("Color" , ColorRGBA .Blue );
135+ geo .setMaterial (mat );
136+ rootNode .attachChild (geo );
79137 }
138+ });
139+
140+ rootNode .attachChild (debugNode );
141+ registerInputMappings ();
142+ }
143+
144+ private void updateHud () {
145+ hud .setText (String .format (Locale .ENGLISH , "TrisPerPixel: %.1f, DistTolerance: %.1f" ,
146+ trisPerPixel , distTolerance ));
147+ }
148+
149+ @ Override
150+ public void onAction (String name , boolean isPressed , float tpf ) {
151+ if (!isPressed ) return ;
152+
153+ if (name .equals ("toggleWireframe" )) {
154+ RenderState renderState = lightMaterial .getAdditionalRenderState ();
155+ boolean wireframe = renderState .isWireframe ();
156+ renderState .setWireframe (!wireframe );
157+ }
158+ if (name .equals ("TrisPerPixel+" )) {
159+ trisPerPixel = FastMath .clamp (trisPerPixel + 0.1f , 0.1f , 5f );
160+ lodControl .setTrisPerPixel (trisPerPixel );
161+ updateHud ();
162+
163+ } else if (name .equals ("TrisPerPixel-" )) {
164+ trisPerPixel = FastMath .clamp (trisPerPixel - 0.1f , 0.1f , 5f );
165+ lodControl .setTrisPerPixel (trisPerPixel );
166+ updateHud ();
167+
168+ } else if (name .equals ("DistTolerance+" )) {
169+ distTolerance = FastMath .clamp (distTolerance + 0.1f , 0.1f , 5f );
170+ lodControl .setDistTolerance (distTolerance );
171+ updateHud ();
172+
173+ } else if (name .equals ("DistTolerance-" )) {
174+ distTolerance = FastMath .clamp (distTolerance - 0.1f , 0.1f , 5f );
175+ lodControl .setDistTolerance (distTolerance );
176+ updateHud ();
80177 }
81- GeometryBatchFactory .optimize (rootNode , true );
82- LodControl control = new LodControl ();
83- rootNode .getChild (0 ).addControl (control );
84- cam .setLocation (new Vector3f (-1.0748308f , 1.35778f , -1.5380064f ));
85- cam .setRotation (new Quaternion (0.18343268f , 0.34531063f , -0.069015436f , 0.9177962f ));
178+ }
86179
180+ private void registerInputMappings () {
181+ addMapping ("toggleWireframe" , new KeyTrigger (KeyInput .KEY_SPACE ));
182+ addMapping ("TrisPerPixel+" , new KeyTrigger (KeyInput .KEY_I ));
183+ addMapping ("TrisPerPixel-" , new KeyTrigger (KeyInput .KEY_K ));
184+ addMapping ("DistTolerance+" , new KeyTrigger (KeyInput .KEY_L ));
185+ addMapping ("DistTolerance-" , new KeyTrigger (KeyInput .KEY_J ));
87186 }
187+
188+ private void addMapping (String mappingName , Trigger ... triggers ) {
189+ inputManager .addMapping (mappingName , triggers );
190+ inputManager .addListener (this , mappingName );
191+ }
192+
193+ private void configureCamera () {
194+ flyCam .setMoveSpeed (25f );
195+ flyCam .setDragToRotate (true );
196+
197+ cam .setLocation (Vector3f .UNIT_XYZ .mult (8f ));
198+ cam .lookAt (Vector3f .ZERO , Vector3f .UNIT_Y );
199+ }
200+
201+ private BitmapText createLabelText (int x , int y , String text ) {
202+ BitmapText bmp = new BitmapText (guiFont );
203+ bmp .setText (text );
204+ bmp .setLocalTranslation (x , settings .getHeight () - y , 0 );
205+ bmp .setColor (ColorRGBA .Red );
206+ guiNode .attachChild (bmp );
207+ return bmp ;
208+ }
209+
88210}
0 commit comments