feat(render): 添加光源与物理系统支持
- 新增 BufferBuilder 工具类用于简化顶点数据提交 - 实现 LightSource 和 LightSourceData 类以支持光源管理- 在 Model2D 中集成光源系统,支持序列化与反序列化 - 扩展 ModelData 以支持物理系统数据的完整序列化 - 重构 ModelRender以支持物理系统应用及碰撞箱渲染 - 添加粒子、弹簧、约束与碰撞体的数据结构与序列化逻辑 - 实现变形器的序列化接口以支持参数驱动动画的持久化
This commit is contained in:
@@ -3,12 +3,8 @@ package com.chuangzhou.vivid2D.test;
|
||||
import com.chuangzhou.vivid2D.render.model.Model2D;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.model.AnimationParameter;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.chuangzhou.vivid2D.render.model.util.AnimationLayer;
|
||||
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem;
|
||||
import com.chuangzhou.vivid2D.render.model.util.ModelPose;
|
||||
import com.chuangzhou.vivid2D.render.model.util.BoundingBox;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Texture;
|
||||
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;
|
||||
@@ -46,7 +42,8 @@ public class ModelTest {
|
||||
testPhysicsSystem();
|
||||
testComplexTransformations();
|
||||
testPerformance();
|
||||
|
||||
Model2D model = createTestModel();
|
||||
printModelState(model);
|
||||
} finally {
|
||||
// Cleanup OpenGL
|
||||
cleanupOpenGL();
|
||||
@@ -55,6 +52,231 @@ public class ModelTest {
|
||||
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
|
||||
*/
|
||||
@@ -586,6 +808,8 @@ public class ModelTest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Utility method to print part hierarchy
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user