Files
window-axis-innovators-box/src/main/java/com/chuangzhou/vivid2D/test/ModelTest.java
tzdwindows 7 8a01020cbe refactor(render):重构关键帧插值器以支持顶点变形- 更新 FrameInterpolator以直接操作 Mesh2D 的一级顶点
- 引入 deformationVertex 参数控制带 VertexTag.DEFORMATION 标签的顶点- 移除对 secondaryVertex 的旧支持及相关冗余代码
- 简化插值计算逻辑并提高角度单位转换容差
- 优化顶点目标计算方法并重命名为 DeformationVertexTarget
- 清理无用的反射回退和安全读取机制- 移除 liquify 工具相关的顶点渲染快捷键控制
- 删除已废弃的 LiquifyTargetPartRander 类文件
-优化导入语句并更新相关类引用路径
2025-11-10 23:12:19 +08:00

825 lines
34 KiB
Java

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);
}
}
}