package com.chuangzhou.vivid2D.test; import com.chuangzhou.vivid2D.render.model.AnimationParameter; import com.chuangzhou.vivid2D.render.model.Mesh2D; import com.chuangzhou.vivid2D.render.model.Model2D; import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.transform.WaveDeformer; import com.chuangzhou.vivid2D.render.model.util.*; import org.joml.Vector2f; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.opengl.GL; import org.lwjgl.system.MemoryUtil; /** * 用于测试Model2D模型的保存和加载功能 * * @author tzdwindows 7 */ public class ModelTest { private static long window; private static boolean glInitialized = false; public static void main(String[] args) { System.out.println("=== Model2D Extended Save and Load Test Start ==="); try { // Initialize OpenGL context for texture testing initializeOpenGL(); // Test 1: Create model and save (with texture) testCreateAndSaveModelWithTexture(); // Test 2: Load model and verify data including textures testLoadAndVerifyModelWithTexture(); // Test 3: Test compressed file operations with textures testCompressedFileOperationsWithTexture(); //testModelSaveLoadIntegrity(model, "test_model.vmdl") // Other existing tests... //testAnimationSystem(); //testPhysicsSystem(); //testComplexTransformations(); //testPerformance(); //Model2D model = createTestModel(); //printModelState(model); } finally { // Cleanup OpenGL cleanupOpenGL(); } System.out.println("=== Model2D Extended Save and Load Test Complete ==="); } public static Model2D createTestModel() { Model2D model = new Model2D("full_test_model"); model.setVersion("1.0.0"); // ==================== 创建部件层级 ==================== ModelPart root = model.createPart("root"); ModelPart body = model.createPart("body"); ModelPart head = model.createPart("head"); ModelPart leftArm = model.createPart("left_arm"); ModelPart rightArm = model.createPart("right_arm"); root.addChild(body); body.addChild(head); body.addChild(leftArm); body.addChild(rightArm); // ==================== 设置本地变换 ==================== root.setPosition(0, 0); root.setRotation(0f); root.setScale(1f, 1f); body.setPosition(0, -50); body.setRotation(10f); // body稍微旋转 body.setScale(1.1f, 1.0f); head.setPosition(0, -50); head.setRotation(-5f); head.setScale(1.0f, 1.0f); leftArm.setPosition(-30, -20); leftArm.setRotation(20f); leftArm.setScale(1.0f, 0.9f); rightArm.setPosition(30, -20); rightArm.setRotation(-20f); rightArm.setScale(1.0f, 0.9f); // ==================== 添加网格 ==================== Mesh2D bodyMesh = Mesh2D.createQuad("body_mesh", 40, 80); Mesh2D headMesh = Mesh2D.createQuad("head_mesh", 50, 50); Mesh2D leftArmMesh = Mesh2D.createQuad("left_arm_mesh", 15, 50); Mesh2D rightArmMesh = Mesh2D.createQuad("right_arm_mesh", 15, 50); model.addMesh(bodyMesh); model.addMesh(headMesh); model.addMesh(leftArmMesh); model.addMesh(rightArmMesh); body.addMesh(bodyMesh); head.addMesh(headMesh); leftArm.addMesh(leftArmMesh); rightArm.addMesh(rightArmMesh); // ==================== 添加纹理 ==================== Texture bodyTex = Texture.createSolidColor("body_tex", 64, 64, 0xFFFF0000); Texture headTex = Texture.createSolidColor("head_tex", 64, 64, 0xFF00FF00); Texture armTex = Texture.createSolidColor("arm_tex", 32, 64, 0xFF0000FF); bodyTex.ensurePixelDataCached(); headTex.ensurePixelDataCached(); armTex.ensurePixelDataCached(); model.addTexture(bodyTex); model.addTexture(headTex); model.addTexture(armTex); bodyMesh.setTexture(bodyTex); headMesh.setTexture(headTex); leftArmMesh.setTexture(armTex); rightArmMesh.setTexture(armTex); // ==================== 添加动画参数 ==================== AnimationParameter smileParam = model.createParameter("smile", 0, 1, 0.5f); AnimationParameter walkParam = model.createParameter("walk", 0, 1, 0); AnimationParameter waveParam = model.createParameter("wave", 0, 1, 0); // ==================== 添加 Deformer ==================== root.addDeformer(new WaveDeformer("blink")); root.addDeformer(new WaveDeformer("wave")); root.addDeformer(new WaveDeformer("blink")); // ==================== 设置元数据 ==================== model.getMetadata().setAuthor("Test Author"); model.getMetadata().setDescription("This is a full-featured test model with transforms and deformers."); model.getMetadata().setLicense("MIT"); model.getMetadata().setFileFormatVersion("1.0.0"); model.getMetadata().setUnitsPerMeter(100.0f); model.getMetadata().setProperty("custom_prop1", "value1"); // ==================== 添加物理 ==================== PhysicsSystem physics = model.getPhysics(); if (physics != null) { physics.initialize(); PhysicsSystem.PhysicsParticle p1 = physics.addParticle("p1", new Vector2f(0, 0), 1f); PhysicsSystem.PhysicsParticle p2 = physics.addParticle("p2", new Vector2f(10, 0), 1f); physics.addSpring("spring1", p1, p2, 10f, 0.5f, 0.1f); } return model; } public static void testModelSaveLoadIntegrity(Model2D model, String filePath) { System.out.println("\n--- Test: Model Save and Load Integrity ---"); try { // 保存模型 model.saveToFile(filePath); // 加载模型 Model2D loaded = Model2D.loadFromFile(filePath); boolean integrityOk = true; // ==================== 基本属性 ==================== if (!model.getName().equals(loaded.getName())) { System.out.println("Name mismatch!"); integrityOk = false; } if (!model.getVersion().equals(loaded.getVersion())) { System.out.println("Version mismatch!"); integrityOk = false; } // ==================== 部件 ==================== if (model.getParts().size() != loaded.getParts().size()) { System.out.println("Parts count mismatch!"); integrityOk = false; } else { for (int i = 0; i < model.getParts().size(); i++) { ModelPart orig = model.getParts().get(i); ModelPart loadPart = loaded.getParts().get(i); if (!orig.getName().equals(loadPart.getName())) { System.out.println("Part name mismatch: " + orig.getName()); integrityOk = false; } // 检查变换 if (!orig.getPosition().equals(loadPart.getPosition()) || orig.getRotation() != loadPart.getRotation() || !orig.getScale().equals(loadPart.getScale())) { System.out.println("Part transform mismatch: " + orig.getName()); integrityOk = false; } // 检查Deformer if (orig.getDeformers().size() != loadPart.getDeformers().size()) { System.out.println("Deformer count mismatch on part: " + orig.getName()); integrityOk = false; } } } // ==================== 网格 ==================== if (model.getMeshes().size() != loaded.getMeshes().size()) { System.out.println("Meshes count mismatch!"); integrityOk = false; } // ==================== 纹理 ==================== if (model.getTextures().size() != loaded.getTextures().size()) { System.out.println("Textures count mismatch!"); integrityOk = false; } // ==================== 参数 ==================== if (model.getParameters().size() != loaded.getParameters().size()) { System.out.println("Parameters count mismatch!"); integrityOk = false; } else { for (String key : model.getParameters().keySet()) { AnimationParameter origParam = model.getParameters().get(key); AnimationParameter loadParam = loaded.getParameters().get(key); if (origParam.getValue() != loadParam.getValue()) { System.out.println("Parameter value mismatch: " + key); integrityOk = false; } } } // ==================== 物理 ==================== PhysicsSystem origPhysics = model.getPhysics(); PhysicsSystem loadPhysics = loaded.getPhysics(); if ((origPhysics != null && loadPhysics == null) || (origPhysics == null && loadPhysics != null)) { System.out.println("Physics system missing after load!"); integrityOk = false; } else if (origPhysics != null) { if (origPhysics.getParticles().size() != loadPhysics.getParticles().size()) { System.out.println("Physics particle count mismatch!"); integrityOk = false; } if (origPhysics.getSprings().size() != loadPhysics.getSprings().size()) { System.out.println("Physics spring count mismatch!"); integrityOk = false; } } System.out.println("Integrity test " + (integrityOk ? "PASSED" : "FAILED")); } catch (Exception e) { System.err.println("Error in testModelSaveLoadIntegrity: " + e.getMessage()); e.printStackTrace(); } } private static void printModelState(Model2D model) { System.out.println(" - Name: " + model.getName()); System.out.println(" - Version: " + model.getVersion()); System.out.println(" - Parts: " + model.getParts().size()); for (ModelPart part : model.getParts()) { printPartHierarchy(part, 1); } System.out.println(" - Parameters:"); for (AnimationParameter param : model.getParameters().values()) { System.out.println(" * " + param.getId() + " = " + param.getValue()); } System.out.println(" - Textures:"); model.getTextures().forEach((k, tex) -> { System.out.println(" * " + tex.getName() + " (" + tex.getWidth() + "x" + tex.getHeight() + ")"); }); System.out.println(" - User Properties:"); model.getMetadata().getUserProperties().forEach((k, v) -> System.out.println(" * " + k + ": " + v) ); } /** * Initialize OpenGL context for texture testing */ private static void initializeOpenGL() { try { // Setup error callback GLFWErrorCallback.createPrint(System.err).set(); // Initialize GLFW if (!GLFW.glfwInit()) { throw new IllegalStateException("Unable to initialize GLFW"); } // Configure GLFW GLFW.glfwDefaultWindowHints(); GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE); // Hide window GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_FALSE); // Create window window = GLFW.glfwCreateWindow(100, 100, "Texture Test", MemoryUtil.NULL, MemoryUtil.NULL); if (window == MemoryUtil.NULL) { throw new RuntimeException("Failed to create GLFW window"); } // Make OpenGL context current GLFW.glfwMakeContextCurrent(window); GLFW.glfwSwapInterval(1); // Enable v-sync // Initialize OpenGL capabilities GL.createCapabilities(); System.out.println("OpenGL initialized successfully"); System.out.println("OpenGL Version: " + org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_VERSION)); glInitialized = true; } catch (Exception e) { System.err.println("Failed to initialize OpenGL: " + e.getMessage()); // Continue without OpenGL for other tests } } /** * Cleanup OpenGL resources */ private static void cleanupOpenGL() { if (window != MemoryUtil.NULL) { GLFW.glfwDestroyWindow(window); } GLFW.glfwTerminate(); GLFW.glfwSetErrorCallback(null).free(); } /** * Test 1: Create model with textures and save to file */ public static void testCreateAndSaveModelWithTexture() { System.out.println("\n--- Test 1: Create and Save Model with Textures ---"); if (!glInitialized) { System.out.println("Skipping texture test - OpenGL not available"); return; } try { // Create model Model2D model = new Model2D("textured_character"); model.setVersion("1.0.0"); // Create parts ModelPart body = model.createPart("body"); ModelPart head = model.createPart("head"); // Build hierarchy body.addChild(head); // Set part properties body.setPosition(0, 0); head.setPosition(0, -50); // Create test textures System.out.println("Creating test textures..."); // Create solid color texture Texture bodyTexture = Texture.createSolidColor("body_texture", 64, 64, 0xFFFF0000); // Red Texture headTexture = Texture.createSolidColor("head_texture", 64, 64, 0xFF00FF00); // Green // Create checkerboard texture Texture checkerTexture = Texture.createCheckerboard("checker_texture", 128, 128, 16, 0xFFFFFFFF, 0xFF0000FF); // White and Blue // === 关键修复:确保纹理数据被缓存 === System.out.println("Ensuring texture data is cached..."); bodyTexture.ensurePixelDataCached(); headTexture.ensurePixelDataCached(); checkerTexture.ensurePixelDataCached(); // 验证缓存状态 System.out.println("Texture cache status:"); System.out.println(" - body_texture: " + (bodyTexture.hasPixelData() ? "CACHED" : "MISSING")); System.out.println(" - head_texture: " + (headTexture.hasPixelData() ? "CACHED" : "MISSING")); System.out.println(" - checker_texture: " + (checkerTexture.hasPixelData() ? "CACHED" : "MISSING")); // Add textures to model model.addTexture(bodyTexture); model.addTexture(headTexture); model.addTexture(checkerTexture); // Create meshes and assign textures Mesh2D bodyMesh = Mesh2D.createQuad("body_mesh", 40, 80); Mesh2D headMesh = Mesh2D.createQuad("head_mesh", 50, 50); // Set textures for meshes bodyMesh.setTexture(bodyTexture); headMesh.setTexture(headTexture); // Add meshes to model and parts model.addMesh(bodyMesh); model.addMesh(headMesh); body.addMesh(bodyMesh); head.addMesh(headMesh); // Create animation parameters AnimationParameter smileParam = model.createParameter("smile", 0, 1, 0); model.setParameterValue("smile", 0.5f); // Update model model.update(0.016f); // Save to regular file String regularFilePath = "textured_character.model"; model.saveToFile(regularFilePath); System.out.println("Textured model saved to regular file: " + regularFilePath); // Save to compressed file String compressedFilePath = "textured_character.model.gz"; model.saveToCompressedFile(compressedFilePath); System.out.println("Textured model saved to compressed file: " + compressedFilePath); // Verify model state before saving System.out.println("Textured model created successfully:"); System.out.println(" - Name: " + model.getName()); System.out.println(" - Textures: " + model.getTextures().size()); System.out.println(" - Meshes: " + model.getMeshes().size()); // Print texture information for (Texture texture : model.getTextures().values()) { System.out.println(" - Texture: " + texture.getName() + " (" + texture.getWidth() + "x" + texture.getHeight() + ", format: " + texture.getFormat() + ", cached: " + texture.hasPixelData() + ")"); } } catch (Exception e) { System.err.println("Error in testCreateAndSaveModelWithTexture: " + e.getMessage()); e.printStackTrace(); // 提供更详细的错误信息 if (e.getCause() != null) { System.err.println("Caused by: " + e.getCause().getMessage()); } } } /** * Test 2: Load model with textures and verify data integrity */ public static void testLoadAndVerifyModelWithTexture() { System.out.println("\n--- Test 2: Load and Verify Model with Textures ---"); if (!glInitialized) { System.out.println("Skipping texture test - OpenGL not available"); return; } try { // Load from regular file String filePath = "textured_character.model"; Model2D loadedModel = Model2D.loadFromFile(filePath); System.out.println("Textured model loaded successfully from: " + filePath); // Verify basic properties System.out.println("Basic properties:"); System.out.println(" - Name: " + loadedModel.getName()); System.out.println(" - Version: " + loadedModel.getVersion()); // Verify textures System.out.println("Textures verification:"); System.out.println(" - Total textures: " + loadedModel.getTextures().size()); for (Texture texture : loadedModel.getTextures().values()) { System.out.println(" - Texture '" + texture.getName() + "': " + texture.getWidth() + "x" + texture.getHeight() + ", format: " + texture.getFormat() + ", disposed: " + texture.isDisposed()); } // Verify parts and meshes System.out.println("Parts and meshes verification:"); for (ModelPart part : loadedModel.getParts()) { System.out.println(" - Part '" + part.getName() + "': " + part.getMeshes().size() + " meshes"); for (Mesh2D mesh : part.getMeshes()) { Texture meshTexture = mesh.getTexture(); System.out.println(" * Mesh '" + mesh.getName() + "': " + (meshTexture != null ? "has texture '" + meshTexture.getName() + "'" : "no texture")); } } // Test texture functionality System.out.println("Texture functionality test:"); Texture bodyTexture = loadedModel.getTexture("body_texture"); if (bodyTexture != null) { System.out.println(" - Body texture validation:"); System.out.println(" * Width: " + bodyTexture.getWidth()); System.out.println(" * Height: " + bodyTexture.getHeight()); System.out.println(" * Format: " + bodyTexture.getFormat()); System.out.println(" * Memory usage: " + bodyTexture.getEstimatedMemoryUsage() + " bytes"); // Test texture binding (if OpenGL context is available) try { bodyTexture.bind(0); System.out.println(" * Texture binding: SUCCESS"); bodyTexture.unbind(); } catch (Exception e) { System.out.println(" * Texture binding: FAILED - " + e.getMessage()); } } // Test parameter modification System.out.println("Parameter modification test:"); loadedModel.setParameterValue("smile", 0.8f); float newSmileValue = loadedModel.getParameterValue("smile"); System.out.println(" - Modified smile parameter to: " + newSmileValue); // Test model update loadedModel.update(0.016f); System.out.println(" - Model update completed successfully"); } catch (Exception e) { System.err.println("Error in testLoadAndVerifyModelWithTexture: " + e.getMessage()); e.printStackTrace(); } } /** * Test 3: Test compressed file operations with textures */ public static void testCompressedFileOperationsWithTexture() { System.out.println("\n--- Test 3: Compressed File Operations with Textures ---"); if (!glInitialized) { System.out.println("Skipping texture test - OpenGL not available"); return; } try { // Load from compressed file String compressedFilePath = "textured_character.model.gz"; Model2D compressedModel = Model2D.loadFromCompressedFile(compressedFilePath); System.out.println("Textured model loaded successfully from compressed file: " + compressedFilePath); System.out.println(" - Name: " + compressedModel.getName()); System.out.println(" - Textures: " + compressedModel.getTextures().size()); System.out.println(" - Parts: " + compressedModel.getParts().size()); // Verify textures in compressed model System.out.println("Compressed model texture verification:"); for (Texture texture : compressedModel.getTextures().values()) { System.out.println(" - Texture '" + texture.getName() + "': " + texture.getWidth() + "x" + texture.getHeight()); } // Modify and re-save compressedModel.setName("modified_textured_character"); compressedModel.setParameterValue("smile", 0.9f); String newCompressedPath = "modified_textured_character.model.gz"; compressedModel.saveToCompressedFile(newCompressedPath); System.out.println("Modified textured model saved to new compressed file: " + newCompressedPath); // Verify the new compressed file can be loaded Model2D reloadedModel = Model2D.loadFromCompressedFile(newCompressedPath); System.out.println("Reloaded modified textured model verification:"); System.out.println(" - Name: " + reloadedModel.getName()); System.out.println(" - Smile parameter value: " + reloadedModel.getParameterValue("smile")); System.out.println(" - Textures: " + reloadedModel.getTextures().size()); } catch (Exception e) { System.err.println("Error in testCompressedFileOperationsWithTexture: " + e.getMessage()); e.printStackTrace(); } } /** * Test 4: Test animation system */ public static void testAnimationSystem() { System.out.println("\n--- Test 4: Animation System Test ---"); try { // Load model Model2D model = Model2D.loadFromFile("test_character.model"); System.out.println("Testing animation system:"); // Test parameter-driven animation System.out.println("Parameter-driven animation test:"); for (int frame = 0; frame < 10; frame++) { float walkValue = (float) Math.sin(frame * 0.2f) * 0.5f + 0.5f; float waveValue = (float) Math.sin(frame * 0.3f); float blinkValue = frame % 20 == 0 ? 1.0f : 0.0f; // Blink every 20 frames model.setParameterValue("walk_cycle", walkValue); model.setParameterValue("wave", waveValue); model.setParameterValue("blink", blinkValue); model.update(0.016f); System.out.println(" - Frame " + frame + ": walk=" + String.format("%.2f", walkValue) + ", wave=" + String.format("%.2f", waveValue) + ", blink=" + String.format("%.2f", blinkValue)); } // Test pose system System.out.println("Pose system test:"); ModelPose currentPose = model.getCurrentPose(); if (currentPose != null) { System.out.println(" - Current pose: " + currentPose); } // Test animation layer blending System.out.println("Animation layer test:"); for (AnimationLayer layer : model.getAnimationLayers()) { System.out.println(" - Layer: " + layer.getName()); } } catch (Exception e) { System.err.println("Error in testAnimationSystem: " + e.getMessage()); e.printStackTrace(); } } /** * Test 5: Test physics system */ public static void testPhysicsSystem() { System.out.println("\n--- Test 5: Physics System Test ---"); try { // Load model Model2D model = Model2D.loadFromFile("test_character.model"); System.out.println("Testing physics system:"); PhysicsSystem physics = model.getPhysics(); System.out.println(" - Physics system: " + (physics != null ? "available" : "not available")); if (physics != null) { Vector2f gravity = physics.getGravity(); System.out.println(" - Gravity: (" + gravity.x + ", " + gravity.y + ")"); System.out.println(" - Air resistance: " + physics.getAirResistance()); System.out.println(" - Time scale: " + physics.getTimeScale()); System.out.println(" - Enabled: " + physics.isEnabled()); } // Test physics simulation System.out.println("Physics simulation test:"); // 初始化物理系统 physics.initialize(); // 添加一些物理粒子 PhysicsSystem.PhysicsParticle particle1 = physics.addParticle("test_particle1", new Vector2f(0, 0), 1.0f); PhysicsSystem.PhysicsParticle particle2 = physics.addParticle("test_particle2", new Vector2f(10, 0), 1.0f); // 添加弹簧连接 physics.addSpring("test_spring", particle1, particle2, 15.0f, 0.5f, 0.1f); for (int step = 0; step < 15; step++) { model.update(0.016f); // Simulate physics if (step % 5 == 0) { System.out.println(" - Step " + step + ": model updated with physics"); Vector2f pos1 = particle1.getPosition(); System.out.println(" Particle1 position: (" + String.format("%.2f", pos1.x) + ", " + String.format("%.2f", pos1.y) + ")"); } } // Test physics properties System.out.println("Physics properties verification:"); System.out.println(" - Active physics: " + physics.hasActivePhysics()); System.out.println(" - Particle count: " + physics.getParticles().size()); System.out.println(" - Spring count: " + physics.getSprings().size()); } catch (Exception e) { System.err.println("Error in testPhysicsSystem: " + e.getMessage()); e.printStackTrace(); } } /** * Test 6: Test complex transformations */ public static void testComplexTransformations() { System.out.println("\n--- Test 6: Complex Transformations Test ---"); try { // Load model Model2D model = Model2D.loadFromFile("test_character.model"); System.out.println("Testing complex transformations:"); // Test nested transformations ModelPart root = model.getRootPart(); if (root != null) { Vector2f position = root.getPosition(); Vector2f scale = root.getScale(); System.out.println("Root transformation:"); System.out.println(" - Local position: (" + position.x + ", " + position.y + ")"); System.out.println(" - Rotation: " + root.getRotation() + " degrees"); System.out.println(" - Scale: (" + scale.x + ", " + scale.y + ")"); // 获取世界变换矩阵中的位置 float worldX = root.getWorldTransform().m02(); float worldY = root.getWorldTransform().m12(); System.out.println(" - World position (from matrix): (" + worldX + ", " + worldY + ")"); } // Test transformation inheritance System.out.println("Transformation inheritance test:"); ModelPart head = model.getPart("head"); if (head != null) { Vector2f headPos = head.getPosition(); float headWorldX = head.getWorldTransform().m02(); float headWorldY = head.getWorldTransform().m12(); System.out.println("Head transformation (relative to body):"); System.out.println(" - Local position: (" + headPos.x + ", " + headPos.y + ")"); System.out.println(" - World position (from matrix): (" + headWorldX + ", " + headWorldY + ")"); } // Test bounds calculation BoundingBox bounds = model.getBounds(); if (bounds != null) { System.out.println("Bounds calculation:"); System.out.println(" - Min: (" + bounds.getMinX() + ", " + bounds.getMinY() + ")"); System.out.println(" - Max: (" + bounds.getMaxX() + ", " + bounds.getMaxY() + ")"); System.out.println(" - Width: " + bounds.getWidth()); System.out.println(" - Height: " + bounds.getHeight()); } // Test visibility system System.out.println("Visibility system test:"); model.setVisible(false); System.out.println(" - Model visible: " + model.isVisible()); model.setVisible(true); System.out.println(" - Model visible: " + model.isVisible()); } catch (Exception e) { System.err.println("Error in testComplexTransformations: " + e.getMessage()); e.printStackTrace(); } } /** * Test 7: Test performance with large model */ public static void testPerformance() { System.out.println("\n--- Test 7: Performance Test ---"); try { // Create a more complex model for performance testing Model2D complexModel = new Model2D("complex_character"); // Add many parts ModelPart root = complexModel.createPart("root"); for (int i = 0; i < 10; i++) { ModelPart part = complexModel.createPart("part_" + i); root.addChild(part); part.setPosition(i * 10, i * 5); part.setRotation(i * 5); // Add mesh Mesh2D mesh = Mesh2D.createQuad("mesh_" + i, 20, 20); complexModel.addMesh(mesh); part.addMesh(mesh); } // Add multiple parameters for (int i = 0; i < 8; i++) { complexModel.createParameter("param_" + i, 0, 1, 0); } System.out.println("Performance test with complex model:"); System.out.println(" - Parts: " + complexModel.getParts().size()); System.out.println(" - Parameters: " + complexModel.getParameters().size()); System.out.println(" - Meshes: " + complexModel.getMeshes().size()); // Performance test: multiple updates long startTime = System.currentTimeMillis(); int frameCount = 100; for (int i = 0; i < frameCount; i++) { // Animate parameters for (int j = 0; j < 8; j++) { float value = (float) Math.sin(i * 0.1f + j * 0.5f) * 0.5f + 0.5f; complexModel.setParameterValue("param_" + j, value); } complexModel.update(0.016f); } long endTime = System.currentTimeMillis(); long totalTime = endTime - startTime; double avgTimePerFrame = (double) totalTime / frameCount; System.out.println("Performance results:"); System.out.println(" - Total time for " + frameCount + " frames: " + totalTime + "ms"); System.out.println(" - Average time per frame: " + String.format("%.2f", avgTimePerFrame) + "ms"); System.out.println(" - Estimated FPS: " + String.format("%.1f", 1000.0 / avgTimePerFrame)); } catch (Exception e) { System.err.println("Error in testPerformance: " + e.getMessage()); e.printStackTrace(); } } /** * Utility method to print part hierarchy */ private static void printPartHierarchy(ModelPart part, int depth) { String indent = " ".repeat(depth); System.out.println(indent + "- " + part.getName() + " (children: " + part.getChildren().size() + ")"); for (ModelPart child : part.getChildren()) { printPartHierarchy(child, depth + 1); } } }