refactor(animation):优化动画系统字段不可变性与getter方法格式- 将AnimationClip中的creationTime字段设为final

- 将AnimationLayer中的parameterOverrides字段设为final
- 将AnimationParameter中的id、defaultValue、minValue、maxValue字段设为final
- 将LightSource中的position、color、intensity字段设为final
- 统一所有getter方法的代码格式,增加换行与大括号
- 优化Mesh2D中部分条件判断逻辑与字段final声明- 调整部分JavaDoc注释格式与空行位置提升可读性
This commit is contained in:
tzdwindows 7
2025-10-25 17:11:51 +08:00
parent 1f5752257e
commit a9c2d202d3
50 changed files with 1342 additions and 703 deletions

View File

@@ -32,7 +32,8 @@ public class ModelLayerPanelTest {
if (person != null) {
try {
person.setOpacity(0.85f);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
}
// 创建 UI
@@ -80,7 +81,8 @@ public class ModelLayerPanelTest {
// 同步通知渲染面板(如果需要)去刷新模型
try {
renderPanel.setModel(model);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
});
bottom.add(refreshBtn);
@@ -138,7 +140,8 @@ public class ModelLayerPanelTest {
// 进程退出(确保彻底关闭)
try {
renderPanel.dispose();
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
System.exit(0);
}
});

View File

@@ -16,11 +16,11 @@ import static org.lwjgl.opengl.GL11.*;
/**
* ModelLoadTest - enhanced debug loader
*
* <p>
* - 加载模型后会自动检查 model 内部结构并打印parts, meshes, textures
* - 尝试把第一个 part 放到窗口中心以确保在可视范围内
* - 每帧确保 ModelRender 的 viewport 与窗口大小一致
*
* <p>
* 运行前请确保 MODEL_PATH 指向你保存的 model 文件
*/
public class ModelLoadTest {
@@ -115,7 +115,7 @@ public class ModelLoadTest {
* 其次尝试创建空实例并调用实例方法 loadFromFile(String)
*/
private void loadModelFromFile(String path) {
inspectSerializedFileStructure( path);
inspectSerializedFileStructure(path);
File f = new File(path);
if (!f.exists()) {
System.err.println("Model file not found: " + path);
@@ -165,7 +165,8 @@ public class ModelLoadTest {
model = inst;
System.out.println("Model loaded via instance method loadFromFile");
return;
} catch (NoSuchMethodException ignored) { }
} catch (NoSuchMethodException ignored) {
}
}
} catch (Throwable t) {
// ignore
@@ -209,35 +210,50 @@ public class ModelLoadTest {
private void printObjectStructure(Object obj, int indent, java.util.Set<Object> seen) {
if (obj == null) {
printIndent(indent); System.out.println("null"); return;
printIndent(indent);
System.out.println("null");
return;
}
if (seen.contains(obj)) {
printIndent(indent); System.out.println("<<already seen " + obj.getClass().getName() + ">>"); return;
printIndent(indent);
System.out.println("<<already seen " + obj.getClass().getName() + ">>");
return;
}
seen.add(obj);
Class<?> cls = obj.getClass();
printIndent(indent); System.out.println(cls.getName());
printIndent(indent);
System.out.println(cls.getName());
// 如果是集合,列出元素类型/数目(不深入过深避免堆栈)
if (obj instanceof java.util.Collection) {
java.util.Collection col = (java.util.Collection) obj;
printIndent(indent+1); System.out.println("size=" + col.size());
if (obj instanceof java.util.Collection col) {
printIndent(indent + 1);
System.out.println("size=" + col.size());
int i = 0;
for (Object e : col) {
if (i++ > 20) { printIndent(indent+1); System.out.println("... (truncated)"); break; }
printObjectStructure(e, indent+1, seen);
if (i++ > 20) {
printIndent(indent + 1);
System.out.println("... (truncated)");
break;
}
printObjectStructure(e, indent + 1, seen);
}
return;
}
if (obj instanceof java.util.Map) {
java.util.Map map = (java.util.Map) obj;
printIndent(indent+1); System.out.println("size=" + map.size());
if (obj instanceof java.util.Map map) {
printIndent(indent + 1);
System.out.println("size=" + map.size());
int i = 0;
for (Object k : map.keySet()) {
if (i++ > 20) { printIndent(indent+1); System.out.println("... (truncated)"); break; }
printIndent(indent+1); System.out.println("Key:");
printObjectStructure(k, indent+2, seen);
printIndent(indent+1); System.out.println("Value:");
printObjectStructure(map.get(k), indent+2, seen);
if (i++ > 20) {
printIndent(indent + 1);
System.out.println("... (truncated)");
break;
}
printIndent(indent + 1);
System.out.println("Key:");
printObjectStructure(k, indent + 2, seen);
printIndent(indent + 1);
System.out.println("Value:");
printObjectStructure(map.get(k), indent + 2, seen);
}
return;
}
@@ -246,12 +262,16 @@ public class ModelLoadTest {
for (java.lang.reflect.Field f : fields) {
f.setAccessible(true);
Object val = null;
try { val = f.get(obj); } catch (Throwable ignored) {}
printIndent(indent+1); System.out.println(f.getName() + " : " + (val == null ? "null" : val.getClass().getName()));
try {
val = f.get(obj);
} catch (Throwable ignored) {
}
printIndent(indent + 1);
System.out.println(f.getName() + " : " + (val == null ? "null" : val.getClass().getName()));
// 对常见自定义类型深入一层
if (val != null && !val.getClass().getName().startsWith("java.") && !val.getClass().isPrimitive()) {
if (val instanceof Number || val instanceof String) continue;
printObjectStructure(val, indent+2, seen);
printObjectStructure(val, indent + 2, seen);
}
}
}
@@ -283,8 +303,7 @@ public class ModelLoadTest {
if (getParts != null) {
Object partsObj = getParts.invoke(model);
if (partsObj instanceof List) {
List<?> parts = (List<?>) partsObj;
if (partsObj instanceof List<?> parts) {
System.out.println("Parts count: " + parts.size());
for (int i = 0; i < parts.size(); i++) {
Object p = parts.get(i);
@@ -296,7 +315,8 @@ public class ModelLoadTest {
Object name = getName != null ? getName.invoke(p) : "<no-name>";
Object pos = getPosition != null ? getPosition.invoke(p) : null;
System.out.println(" name=" + name + ", pos=" + pos);
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
}
// 如果 parts 不为空,尝试把第一个 part 放到窗口中心(如果有 setPosition
@@ -305,7 +325,7 @@ public class ModelLoadTest {
try {
Method setPosition = tryGetMethod(first.getClass(), "setPosition", float.class, float.class);
if (setPosition != null) {
setPosition.invoke(first, (float)WINDOW_WIDTH/2f, (float)WINDOW_HEIGHT/2f);
setPosition.invoke(first, (float) WINDOW_WIDTH / 2f, (float) WINDOW_HEIGHT / 2f);
System.out.println("Moved first part to window center.");
}
} catch (Throwable t) {
@@ -317,8 +337,7 @@ public class ModelLoadTest {
if (getMeshes != null) {
Object meshesObj = getMeshes.invoke(model);
if (meshesObj instanceof List) {
List<?> meshes = (List<?>) meshesObj;
if (meshesObj instanceof List<?> meshes) {
System.out.println("Meshes count: " + meshes.size());
for (int i = 0; i < Math.min(meshes.size(), 10); i++) {
Object m = meshes.get(i);
@@ -330,15 +349,15 @@ public class ModelLoadTest {
Object vc = getVertexCount != null ? getVertexCount.invoke(m) : null;
Object tex = getTexture != null ? getTexture.invoke(m) : null;
System.out.println(" vertexCount=" + vc + ", texture=" + tex);
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
}
}
}
if (getTextures != null) {
Object texObj = getTextures.invoke(model);
if (texObj instanceof List) {
List<?> texs = (List<?>) texObj;
if (texObj instanceof List<?> texs) {
System.out.println("Textures count: " + texs.size());
for (int i = 0; i < Math.min(texs.size(), 10); i++) {
Object t = texs.get(i);
@@ -351,7 +370,8 @@ public class ModelLoadTest {
Object h = getH != null ? getH.invoke(t) : "?";
Object id = getId != null ? getId.invoke(t) : "?";
System.out.println(" size=" + w + "x" + h + ", id=" + id);
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
}
}
}
@@ -437,8 +457,8 @@ public class ModelLoadTest {
try {
ModelRender.cleanup();
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
if (window != MemoryUtil.NULL) {

View File

@@ -8,7 +8,6 @@ import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.Texture;
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
@@ -22,6 +21,7 @@ import java.util.Random;
/**
* ModelRenderLightingTest
* 测试使用 Model2D + 光源进行简单光照渲染
*
* @author tzdwindows 7
*/
public class ModelRenderLightingTest {
@@ -34,7 +34,7 @@ public class ModelRenderLightingTest {
private boolean running = true;
private Model2D model;
private Random random = new Random();
private final Random random = new Random();
private float animationTime = 0f;
@@ -115,7 +115,7 @@ public class ModelRenderLightingTest {
rightArm.setPosition(60, -20);
Mesh2D rightArmMesh = Mesh2D.createQuad("right_arm_mesh", 18, 90);
rightArmMesh.setTexture(createSolidTexture(16, 90, 0xFF6495ED));
rightArmMesh.setSelected( true);
rightArmMesh.setSelected(true);
rightArm.addMesh(rightArmMesh);
// legs
@@ -152,11 +152,11 @@ public class ModelRenderLightingTest {
private Texture createSolidTexture(int w, int h, int rgba) {
ByteBuffer buf = MemoryUtil.memAlloc(w * h * 4);
byte a = (byte)((rgba >> 24) & 0xFF);
byte r = (byte)((rgba >> 16) & 0xFF);
byte g = (byte)((rgba >> 8) & 0xFF);
byte b = (byte)(rgba & 0xFF);
for(int i=0;i<w*h;i++) buf.put(r).put(g).put(b).put(a);
byte a = (byte) ((rgba >> 24) & 0xFF);
byte r = (byte) ((rgba >> 16) & 0xFF);
byte g = (byte) ((rgba >> 8) & 0xFF);
byte b = (byte) (rgba & 0xFF);
for (int i = 0; i < w * h; i++) buf.put(r).put(g).put(b).put(a);
buf.flip();
Texture t = new Texture("solid_" + rgba, w, h, Texture.TextureFormat.RGBA, buf);
MemoryUtil.memFree(buf);
@@ -166,16 +166,16 @@ public class ModelRenderLightingTest {
private Texture createHeadTexture() {
int width = 64, height = 64;
int[] pixels = new int[width * height];
for(int y=0;y<height;y++) {
for(int x=0;x<width;x++) {
float dx = (x - width/2f)/(width/2f);
float dy = (y - height/2f)/(height/2f);
float dist = (float)Math.sqrt(dx*dx + dy*dy);
int alpha = dist>1.0f?0:255;
int r = (int)(240*(1f - dist*0.25f));
int g = (int)(200*(1f - dist*0.25f));
int b = (int)(180*(1f - dist*0.25f));
pixels[y*width + x] = (alpha<<24)|(r<<16)|(g<<8)|b;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float dx = (x - width / 2f) / (width / 2f);
float dy = (y - height / 2f) / (height / 2f);
float dist = (float) Math.sqrt(dx * dx + dy * dy);
int alpha = dist > 1.0f ? 0 : 255;
int r = (int) (240 * (1f - dist * 0.25f));
int g = (int) (200 * (1f - dist * 0.25f));
int b = (int) (180 * (1f - dist * 0.25f));
pixels[y * width + x] = (alpha << 24) | (r << 16) | (g << 8) | b;
}
}
return new Texture("head_tex", width, height, Texture.TextureFormat.RGBA, pixels);
@@ -188,11 +188,11 @@ public class ModelRenderLightingTest {
while (running && !GLFW.glfwWindowShouldClose(window)) {
long now = System.nanoTime();
accumulator += (now - last)/nsPerUpdate;
accumulator += (now - last) / nsPerUpdate;
last = now;
while (accumulator >= 1.0) {
update(1.0f/60.0f);
update(1.0f / 60.0f);
accumulator -= 1.0;
}
@@ -204,9 +204,9 @@ public class ModelRenderLightingTest {
private void update(float dt) {
animationTime += dt;
float armSwing = (float)Math.sin(animationTime*3f)*0.7f;
float legSwing = (float)Math.sin(animationTime*3f + Math.PI)*0.6f;
float headRot = (float)Math.sin(animationTime*1.4f)*0.15f;
float armSwing = (float) Math.sin(animationTime * 3f) * 0.7f;
float legSwing = (float) Math.sin(animationTime * 3f + Math.PI) * 0.6f;
float headRot = (float) Math.sin(animationTime * 1.4f) * 0.15f;
ModelPart leftArm = model.getPart("left_arm");
ModelPart rightArm = model.getPart("right_arm");
@@ -214,24 +214,24 @@ public class ModelRenderLightingTest {
ModelPart rightLeg = model.getPart("right_leg");
ModelPart head = model.getPart("head");
if(leftArm!=null) leftArm.setRotation(-0.8f*armSwing - 0.2f);
if(rightArm!=null) rightArm.setRotation(0.8f*armSwing + 0.2f);
if(leftLeg!=null) leftLeg.setRotation(0.6f*legSwing);
if(rightLeg!=null) rightLeg.setRotation(-0.6f*legSwing);
if(head!=null) head.setRotation(headRot);
if (leftArm != null) leftArm.setRotation(-0.8f * armSwing - 0.2f);
if (rightArm != null) rightArm.setRotation(0.8f * armSwing + 0.2f);
if (leftLeg != null) leftLeg.setRotation(0.6f * legSwing);
if (rightLeg != null) rightLeg.setRotation(-0.6f * legSwing);
if (head != null) head.setRotation(headRot);
model.update(dt);
}
private void render() {
RenderSystem.setClearColor(0.18f,0.18f,0.25f,1.0f);
ModelRender.render(1f/60f, model);
RenderSystem.setClearColor(0.18f, 0.18f, 0.25f, 1.0f);
ModelRender.render(1f / 60f, model);
}
private void cleanup() {
ModelRender.cleanup();
Texture.cleanupAll();
if(window!= MemoryUtil.NULL) GLFW.glfwDestroyWindow(window);
if (window != MemoryUtil.NULL) GLFW.glfwDestroyWindow(window);
GLFW.glfwTerminate();
GLFW.glfwSetErrorCallback(null).free();
}

View File

@@ -21,7 +21,6 @@ import java.util.Random;
* 重写后的 ModelRender 测试示例:构造一个简单的人形(头、身体、左右手、左右腿)
* 便于验证层级变换与渲染是否正确。
*
*
* @author tzdwindows 7
*/
public class ModelRenderTest {
@@ -34,7 +33,7 @@ public class ModelRenderTest {
private boolean running = true;
private Model2D testModel;
private Random random = new Random();
private final Random random = new Random();
private float animationTime = 0f;
private boolean animate = true;

View File

@@ -6,8 +6,6 @@ import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.Texture;
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import org.joml.Matrix3f;
import org.joml.Vector2f;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
@@ -18,6 +16,7 @@ import java.nio.ByteBuffer;
/**
* 用于测试中心点旋转
*
* @author tzdwindows 7
*/
public class ModelRenderTest2 {
@@ -32,6 +31,7 @@ public class ModelRenderTest2 {
private float rotationAngle = 0f;
private int testCase = 0;
private Mesh2D squareMesh;
public static void main(String[] args) {
new ModelRenderTest2().run();
}
@@ -104,7 +104,7 @@ public class ModelRenderTest2 {
ModelPart square = testModel.createPart("square");
square.setPosition(0, 0); // center of window
square.setPivot(0,0);
square.setPivot(0, 0);
// Create 80x80 quad centered at origin
squareMesh = Mesh2D.createQuad("square_mesh", 80, 80);
// Shift vertices so center is at (0,0)

View File

@@ -5,7 +5,6 @@ import com.chuangzhou.vivid2D.render.model.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.Texture;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryUtil;
@@ -15,6 +14,7 @@ import static org.lwjgl.opengl.GL11.*;
/**
* Texture Render Test Class - Debug Version
*
* @author tzdwindows 7
*/
public class ModelRenderTextureTest {
@@ -143,10 +143,10 @@ public class ModelRenderTextureTest {
// 回退到手动创建顶点
System.out.println("Using manual vertex creation");
float[] vertices = {
-width/2, -height/2, // bottom left
width/2, -height/2, // bottom right
width/2, height/2, // top right
-width/2, height/2 // top left
-width / 2, -height / 2, // bottom left
width / 2, -height / 2, // bottom right
width / 2, height / 2, // top right
-width / 2, height / 2 // top left
};
float[] uvs = {
@@ -203,10 +203,10 @@ public class ModelRenderTextureTest {
} catch (Exception e) {
// 手动创建
float[] vertices = {
-width/2, -height/2,
width/2, -height/2,
width/2, height/2,
-width/2, height/2
-width / 2, -height / 2,
width / 2, -height / 2,
width / 2, height / 2,
-width / 2, height / 2
};
float[] uvs = {
@@ -320,11 +320,16 @@ public class ModelRenderTextureTest {
private String getGLErrorString(int error) {
switch (error) {
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
default: return "Unknown Error (0x" + Integer.toHexString(error) + ")";
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
default:
return "Unknown Error (0x" + Integer.toHexString(error) + ")";
}
}

View File

@@ -1,8 +1,8 @@
package com.chuangzhou.vivid2D.test;
import com.chuangzhou.vivid2D.render.model.AnimationParameter;
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.transform.WaveDeformer;
import com.chuangzhou.vivid2D.render.model.util.*;
import org.joml.Vector2f;
@@ -809,7 +809,6 @@ public class ModelTest {
}
/**
* Utility method to print part hierarchy
*/

View File

@@ -4,8 +4,8 @@ import com.chuangzhou.vivid2D.render.ModelRender;
import com.chuangzhou.vivid2D.render.model.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.Texture;
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem;
import com.chuangzhou.vivid2D.render.model.util.Texture;
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import org.joml.Vector2f;
import org.lwjgl.glfw.GLFW;
@@ -20,6 +20,7 @@ import java.util.List;
/**
* 物理系统使用实例 - 演示弹簧、重力和碰撞效果
*
* @author tzdwindows 7
*/
public class ModelTest2 {
@@ -39,7 +40,7 @@ public class ModelTest2 {
private boolean springsEnabled = true;
// 存储部件引用,用于清理
private List<ModelPart> currentParts = new ArrayList<>();
private final List<ModelPart> currentParts = new ArrayList<>();
// 所有测试基点(初始 xy = 0,0
private final Vector2f initialOrigin = new Vector2f(0, 0);
@@ -598,13 +599,20 @@ public class ModelTest2 {
*/
private String getTestCaseName(int testCase) {
switch (testCase) {
case 0: return "Spring Chain";
case 1: return "Cloth Simulation";
case 2: return "Pendulum System";
case 3: return "Soft Body";
case 4: return "Wind Test";
case 5: return "Free Fall Test";
default: return "Unknown";
case 0:
return "Spring Chain";
case 1:
return "Cloth Simulation";
case 2:
return "Pendulum System";
case 3:
return "Soft Body";
case 4:
return "Wind Test";
case 5:
return "Free Fall Test";
default:
return "Unknown";
}
}

View File

@@ -15,6 +15,7 @@ import java.nio.ByteBuffer;
/**
* 在原 TestModelGLPanel 的基础上增加简单动画(手臂、腿、头部摆动)
*
* @author tzdwindows 7
*/
public class TestModelGLPanel {