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:12:21 +08:00
parent a9c2d202d3
commit 3add504321
14 changed files with 558 additions and 224 deletions

View File

@@ -6,7 +6,10 @@ import com.chuangzhou.vivid2D.render.systems.buffer.Tesselator;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement; import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram; import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
import org.joml.Vector4f; import org.joml.Vector4f;
import org.lwjgl.opengl.*; import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL33;
import org.lwjgl.stb.STBTTAlignedQuad; import org.lwjgl.stb.STBTTAlignedQuad;
import org.lwjgl.stb.STBTTBakedChar; import org.lwjgl.stb.STBTTBakedChar;
import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryStack;
@@ -15,10 +18,12 @@ import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static org.lwjgl.stb.STBTruetype.*; import static org.lwjgl.stb.STBTruetype.stbtt_BakeFontBitmap;
import static org.lwjgl.stb.STBTruetype.stbtt_GetBakedQuad;
/** /**
* 支持 ASCII + 中文的 OpenGL 文本渲染器 * 支持 ASCII + 中文的 OpenGL 文本渲染器
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public final class TextRenderer { public final class TextRenderer {
@@ -317,7 +322,15 @@ public final class TextRenderer {
logger.debug("TextRenderer cleaned up"); logger.debug("TextRenderer cleaned up");
} }
public boolean isInitialized() { return initialized; } public boolean isInitialized() {
public int getAsciiTextureId() { return asciiTextureId; } return initialized;
public int getChineseTextureId() { return chineseTextureId; } }
public int getAsciiTextureId() {
return asciiTextureId;
}
public int getChineseTextureId() {
return chineseTextureId;
}
} }

View File

@@ -94,7 +94,10 @@ public class PsdParser {
} }
} finally { } finally {
try { reader.dispose(); } catch (Exception ignored) {} try {
reader.dispose();
} catch (Exception ignored) {
}
} }
return result; return result;

View File

@@ -2,6 +2,7 @@ package com.chuangzhou.vivid2D.render.model;
/** /**
* 模型事件 * 模型事件
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public interface ModelEvent { public interface ModelEvent {

View File

@@ -9,6 +9,7 @@ import java.io.Serializable;
/** /**
* LightSource 的序列化数据类(扩展:包含辉光/Glow 的序列化字段) * LightSource 的序列化数据类(扩展:包含辉光/Glow 的序列化字段)
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class LightSourceData implements Serializable { public class LightSourceData implements Serializable {

View File

@@ -80,8 +80,7 @@ public class PartData implements Serializable {
// 期望的方法签名: public List<YourStrokeClass> getLiquifyStrokes() // 期望的方法签名: public List<YourStrokeClass> getLiquifyStrokes()
java.lang.reflect.Method m = part.getClass().getMethod("getLiquifyStrokes"); java.lang.reflect.Method m = part.getClass().getMethod("getLiquifyStrokes");
Object strokesObj = m.invoke(part); Object strokesObj = m.invoke(part);
if (strokesObj instanceof List<?>) { if (strokesObj instanceof List<?> strokes) {
List<?> strokes = (List<?>) strokesObj;
for (Object s : strokes) { for (Object s : strokes) {
// 支持两种情况:存储为自定义类型(有 getMode/getRadius/getStrength/getIterations/getPoints 方法) // 支持两种情况:存储为自定义类型(有 getMode/getRadius/getStrength/getIterations/getPoints 方法)
// 或者直接存储为通用 Map/POJO。我们做宽松处理通过反射尽可能读取常见字段。 // 或者直接存储为通用 Map/POJO。我们做宽松处理通过反射尽可能读取常见字段。
@@ -91,37 +90,39 @@ public class PartData implements Serializable {
java.lang.reflect.Method gm = s.getClass().getMethod("getMode"); java.lang.reflect.Method gm = s.getClass().getMethod("getMode");
Object modeObj = gm.invoke(s); Object modeObj = gm.invoke(s);
if (modeObj != null) strokeData.mode = modeObj.toString(); if (modeObj != null) strokeData.mode = modeObj.toString();
} catch (NoSuchMethodException ignored) {} } catch (NoSuchMethodException ignored) {
}
try { try {
java.lang.reflect.Method gr = s.getClass().getMethod("getRadius"); java.lang.reflect.Method gr = s.getClass().getMethod("getRadius");
Object r = gr.invoke(s); Object r = gr.invoke(s);
if (r instanceof Number) strokeData.radius = ((Number) r).floatValue(); if (r instanceof Number) strokeData.radius = ((Number) r).floatValue();
} catch (NoSuchMethodException ignored) {} } catch (NoSuchMethodException ignored) {
}
try { try {
java.lang.reflect.Method gs = s.getClass().getMethod("getStrength"); java.lang.reflect.Method gs = s.getClass().getMethod("getStrength");
Object st = gs.invoke(s); Object st = gs.invoke(s);
if (st instanceof Number) strokeData.strength = ((Number) st).floatValue(); if (st instanceof Number) strokeData.strength = ((Number) st).floatValue();
} catch (NoSuchMethodException ignored) {} } catch (NoSuchMethodException ignored) {
}
try { try {
java.lang.reflect.Method gi = s.getClass().getMethod("getIterations"); java.lang.reflect.Method gi = s.getClass().getMethod("getIterations");
Object it = gi.invoke(s); Object it = gi.invoke(s);
if (it instanceof Number) strokeData.iterations = ((Number) it).intValue(); if (it instanceof Number) strokeData.iterations = ((Number) it).intValue();
} catch (NoSuchMethodException ignored) {} } catch (NoSuchMethodException ignored) {
}
// 读取点列表 // 读取点列表
try { try {
java.lang.reflect.Method gp = s.getClass().getMethod("getPoints"); java.lang.reflect.Method gp = s.getClass().getMethod("getPoints");
Object ptsObj = gp.invoke(s); Object ptsObj = gp.invoke(s);
if (ptsObj instanceof List<?>) { if (ptsObj instanceof List<?> pts) {
List<?> pts = (List<?>) ptsObj;
for (Object p : pts) { for (Object p : pts) {
// 支持 Vector2f 或自定义点类型(有 getX/getY/getPressure // 支持 Vector2f 或自定义点类型(有 getX/getY/getPressure
LiquifyPointData pd = new LiquifyPointData(); LiquifyPointData pd = new LiquifyPointData();
if (p instanceof org.joml.Vector2f) { if (p instanceof Vector2f v) {
org.joml.Vector2f v = (org.joml.Vector2f) p;
pd.x = v.x; pd.x = v.x;
pd.y = v.y; pd.y = v.y;
pd.pressure = 1.0f; pd.pressure = 1.0f;
@@ -144,8 +145,7 @@ public class PartData implements Serializable {
} }
} catch (NoSuchMethodException ex) { } catch (NoSuchMethodException ex) {
// 最后尝试 Map 形式(键 x,y // 最后尝试 Map 形式(键 x,y
if (p instanceof Map<?, ?>) { if (p instanceof Map<?, ?> mapP) {
Map<?, ?> mapP = (Map<?, ?>) p;
Object ox = mapP.get("x"); Object ox = mapP.get("x");
Object oy = mapP.get("y"); Object oy = mapP.get("y");
if (ox instanceof Number && oy instanceof Number) { if (ox instanceof Number && oy instanceof Number) {
@@ -160,7 +160,8 @@ public class PartData implements Serializable {
strokeData.points.add(pd); strokeData.points.add(pd);
} }
} }
} catch (NoSuchMethodException ignored) {} } catch (NoSuchMethodException ignored) {
}
// 如果没有 mode则用默认 PUSH // 如果没有 mode则用默认 PUSH
if (strokeData.mode == null) strokeData.mode = ModelPart.LiquifyMode.PUSH.name(); if (strokeData.mode == null) strokeData.mode = ModelPart.LiquifyMode.PUSH.name();
@@ -236,7 +237,8 @@ public class PartData implements Serializable {
ModelPart.LiquifyMode modeEnum = ModelPart.LiquifyMode.PUSH; ModelPart.LiquifyMode modeEnum = ModelPart.LiquifyMode.PUSH;
try { try {
modeEnum = ModelPart.LiquifyMode.valueOf(stroke.mode); modeEnum = ModelPart.LiquifyMode.valueOf(stroke.mode);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
// 对每个点进行重放:调用 applyLiquify存在于 ModelPart // 对每个点进行重放:调用 applyLiquify存在于 ModelPart
if (stroke.points != null) { if (stroke.points != null) {

View File

@@ -5,11 +5,11 @@ import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.SaveVector2f; import com.chuangzhou.vivid2D.render.model.util.SaveVector2f;
import org.joml.Vector2f; import org.joml.Vector2f;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* 缩放变形器 - 围绕中心点缩放顶点 * 缩放变形器 - 围绕中心点缩放顶点
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class ScaleDeformer extends Deformer { public class ScaleDeformer extends Deformer {
@@ -94,11 +94,23 @@ public class ScaleDeformer extends Deformer {
} }
// Getter/Setter // Getter/Setter
public Vector2f getBaseScale() { return new Vector2f(baseScale); } public Vector2f getBaseScale() {
public void setBaseScale(Vector2f baseScale) { this.baseScale.set(baseScale); } return new Vector2f(baseScale);
}
public Vector2f getScaleRange() { return new Vector2f(scaleRange); }
public void setScaleRange(Vector2f scaleRange) { this.scaleRange.set(scaleRange); } public void setBaseScale(Vector2f baseScale) {
this.baseScale.set(baseScale);
public Vector2f getCurrentScale() { return new Vector2f(currentScale); } }
public Vector2f getScaleRange() {
return new Vector2f(scaleRange);
}
public void setScaleRange(Vector2f scaleRange) {
this.scaleRange.set(scaleRange);
}
public Vector2f getCurrentScale() {
return new Vector2f(currentScale);
}
} }

View File

@@ -25,7 +25,7 @@ public class AnimationClip {
// ==================== 元数据 ==================== // ==================== 元数据 ====================
private String author; private String author;
private String description; private String description;
private long creationTime; private final long creationTime;
private long lastModifiedTime; private long lastModifiedTime;
private Map<String, String> userData; private Map<String, String> userData;
@@ -554,9 +554,17 @@ public class AnimationClip {
} }
// Getter方法 // Getter方法
public String getParameterId() { return parameterId; } public String getParameterId() {
public List<Keyframe> getKeyframes() { return Collections.unmodifiableList(keyframes); } return parameterId;
public float getDefaultValue() { return defaultValue; } }
public List<Keyframe> getKeyframes() {
return Collections.unmodifiableList(keyframes);
}
public float getDefaultValue() {
return defaultValue;
}
} }
/** /**
@@ -578,9 +586,17 @@ public class AnimationClip {
} }
// Getter方法 // Getter方法
public float getTime() { return time; } public float getTime() {
public float getValue() { return value; } return time;
public InterpolationType getInterpolation() { return interpolation; } }
public float getValue() {
return value;
}
public InterpolationType getInterpolation() {
return interpolation;
}
@Override @Override
public String toString() { public String toString() {
@@ -621,10 +637,21 @@ public class AnimationClip {
} }
// Getter方法 // Getter方法
public String getName() { return name; } public String getName() {
public float getTime() { return time; } return name;
public Runnable getAction() { return action; } }
public boolean isTriggered() { return triggered; }
public float getTime() {
return time;
}
public Runnable getAction() {
return action;
}
public boolean isTriggered() {
return triggered;
}
@Override @Override
public String toString() { public String toString() {
@@ -646,23 +673,39 @@ public class AnimationClip {
// ==================== Getter/Setter ==================== // ==================== Getter/Setter ====================
public String getName() { return name; } public String getName() {
public UUID getUuid() { return uuid; } return name;
}
public UUID getUuid() {
return uuid;
}
public float getDuration() {
return duration;
}
public float getDuration() { return duration; }
public void setDuration(float duration) { public void setDuration(float duration) {
this.duration = Math.max(0.0f, duration); this.duration = Math.max(0.0f, duration);
markModified(); markModified();
} }
public float getFramesPerSecond() { return framesPerSecond; } public float getFramesPerSecond() {
return framesPerSecond;
}
public void setFramesPerSecond(float framesPerSecond) { public void setFramesPerSecond(float framesPerSecond) {
this.framesPerSecond = Math.max(1.0f, framesPerSecond); this.framesPerSecond = Math.max(1.0f, framesPerSecond);
markModified(); markModified();
} }
public boolean isLooping() { return looping; } public boolean isLooping() {
public void setLooping(boolean looping) { this.looping = looping; } return looping;
}
public void setLooping(boolean looping) {
this.looping = looping;
}
public Map<String, AnimationCurve> getCurves() { public Map<String, AnimationCurve> getCurves() {
return Collections.unmodifiableMap(curves); return Collections.unmodifiableMap(curves);
@@ -676,18 +719,34 @@ public class AnimationClip {
return Collections.unmodifiableMap(defaultValues); return Collections.unmodifiableMap(defaultValues);
} }
public String getAuthor() { return author; } public String getAuthor() {
public void setAuthor(String author) { this.author = author; } return author;
}
public String getDescription() { return description; } public void setAuthor(String author) {
public void setDescription(String description) { this.description = description; } this.author = author;
}
public long getCreationTime() { return creationTime; } public String getDescription() {
public long getLastModifiedTime() { return lastModifiedTime; } return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getCreationTime() {
return creationTime;
}
public long getLastModifiedTime() {
return lastModifiedTime;
}
public Map<String, String> getUserData() { public Map<String, String> getUserData() {
return Collections.unmodifiableMap(userData); return Collections.unmodifiableMap(userData);
} }
public void setUserData(Map<String, String> userData) { public void setUserData(Map<String, String> userData) {
this.userData = new ConcurrentHashMap<>(userData); this.userData = new ConcurrentHashMap<>(userData);
} }

View File

@@ -516,10 +516,21 @@ public class BoundingBox {
// ==================== Getter方法 ==================== // ==================== Getter方法 ====================
public float getMinX() { return minX; } public float getMinX() {
public float getMinY() { return minY; } return minX;
public float getMaxX() { return maxX; } }
public float getMaxY() { return maxY; }
public float getMinY() {
return minY;
}
public float getMaxX() {
return maxX;
}
public float getMaxY() {
return maxY;
}
public float getWidth() { public float getWidth() {
return valid ? maxX - minX : 0.0f; return valid ? maxX - minX : 0.0f;
@@ -529,12 +540,25 @@ public class BoundingBox {
return valid ? maxY - minY : 0.0f; return valid ? maxY - minY : 0.0f;
} }
public float getLeft() { return minX; } public float getLeft() {
public float getRight() { return maxX; } return minX;
public float getBottom() { return minY; } }
public float getTop() { return maxY; }
public boolean isValid() { return valid; } public float getRight() {
return maxX;
}
public float getBottom() {
return minY;
}
public float getTop() {
return maxY;
}
public boolean isValid() {
return valid;
}
// ==================== 静态工厂方法 ==================== // ==================== 静态工厂方法 ====================

View File

@@ -11,6 +11,14 @@ import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram; import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
import org.joml.Matrix3f; import org.joml.Matrix3f;
import org.joml.Vector2f; import org.joml.Vector2f;
import org.joml.Vector4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL30;
import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
@@ -19,12 +27,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import org.joml.Vector4f;
import org.lwjgl.opengl.*;
import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* 2D网格类用于存储和管理2D模型的几何数据 * 2D网格类用于存储和管理2D模型的几何数据
* 支持顶点、UV坐标、索引和变形操作 * 支持顶点、UV坐标、索引和变形操作
@@ -42,7 +44,7 @@ public class Mesh2D {
private ModelPart modelPart; private ModelPart modelPart;
// ==================== 二级顶点支持 ==================== // ==================== 二级顶点支持 ====================
private List<SecondaryVertex> secondaryVertices = new ArrayList<>(); private final List<SecondaryVertex> secondaryVertices = new ArrayList<>();
private boolean showSecondaryVertices = false; private boolean showSecondaryVertices = false;
public Vector4f secondaryVertexColor = new Vector4f(0.0f, 1.0f, 0.0f, 1.0f); // 绿色二级顶点 public Vector4f secondaryVertexColor = new Vector4f(0.0f, 1.0f, 0.0f, 1.0f); // 绿色二级顶点
public Vector4f selectedSecondaryVertexColor = new Vector4f(1.0f, 0.0f, 0.0f, 1.0f); // 红色选中的二级顶点 public Vector4f selectedSecondaryVertexColor = new Vector4f(1.0f, 0.0f, 0.0f, 1.0f); // 红色选中的二级顶点
@@ -77,14 +79,14 @@ public class Mesh2D {
// ==================== 液化状态渲染 ==================== // ==================== 液化状态渲染 ====================
public boolean showLiquifyOverlay = false; public boolean showLiquifyOverlay = false;
private Vector4f liquifyOverlayColor = new Vector4f(1.0f, 0.5f, 0.0f, 0.3f); // 半透明橙色 private final Vector4f liquifyOverlayColor = new Vector4f(1.0f, 0.5f, 0.0f, 0.3f); // 半透明橙色
// ==================== 木偶工具 ==================== // ==================== 木偶工具 ====================
private List<PuppetPin> puppetPins = new ArrayList<>(); private final List<PuppetPin> puppetPins = new ArrayList<>();
private PuppetPin selectedPuppetPin = null; private PuppetPin selectedPuppetPin = null;
private boolean showPuppetPins = true; private boolean showPuppetPins = true;
private Vector4f puppetPinColor = new Vector4f(1.0f, 0.0f, 0.0f, 1.0f); // 红色控制点 private final Vector4f puppetPinColor = new Vector4f(1.0f, 0.0f, 0.0f, 1.0f); // 红色控制点
private Vector4f selectedPuppetPinColor = new Vector4f(1.0f, 1.0f, 0.0f, 1.0f); // 黄色选中的控制点 private final Vector4f selectedPuppetPinColor = new Vector4f(1.0f, 1.0f, 0.0f, 1.0f); // 黄色选中的控制点
private float puppetPinSize = 8.0f; private float puppetPinSize = 8.0f;
// ==================== 常量 ==================== // ==================== 常量 ====================
@@ -377,6 +379,7 @@ public class Mesh2D {
public boolean getShowPuppetPins() { public boolean getShowPuppetPins() {
return showPuppetPins; return showPuppetPins;
} }
/** /**
* 绘制木偶控制点 * 绘制木偶控制点
*/ */
@@ -2296,11 +2299,7 @@ public class Mesh2D {
} }
float squaredLength = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); float squaredLength = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
if (dot > squaredLength) { return !(dot > squaredLength); // 在线段终点之后
return false; // 在线段终点之后
}
return true;
} }
public boolean containsPoint(Vector2f point) { public boolean containsPoint(Vector2f point) {
@@ -2385,6 +2384,7 @@ public class Mesh2D {
} }
// ==================== 状态管理 ==================== // ==================== 状态管理 ====================
/** /**
* 标记数据已修改 * 标记数据已修改
*/ */
@@ -2614,8 +2614,7 @@ public class Mesh2D {
// 恢复之前的 program如果我们切换过 // 恢复之前的 program如果我们切换过
if (switchedProgram) { if (switchedProgram) {
if (prevProgram != 0) RenderSystem.useProgram(prevProgram); RenderSystem.useProgram(prevProgram);
else RenderSystem.useProgram(0);
} }
// 选中框绘制(需要切换到固色 shader // 选中框绘制(需要切换到固色 shader
@@ -3039,6 +3038,7 @@ public class Mesh2D {
bb.vertex(centerX - arrowSize, rotationHandleY + arrowSize, 0.0f, 0.0f); bb.vertex(centerX - arrowSize, rotationHandleY + arrowSize, 0.0f, 0.0f);
bb.endImmediate(); bb.endImmediate();
} }
private void drawCenterPoint(BufferBuilder bb, float minX, float minY, float maxX, float maxY) { private void drawCenterPoint(BufferBuilder bb, float minX, float minY, float maxX, float maxY) {
// 使用 Mesh2D 的 pivot 作为中心点位置,但当 pivot 不在 bounds 内时回退为 bounds 中心(避免渲染时跳回 0,0 的情况) // 使用 Mesh2D 的 pivot 作为中心点位置,但当 pivot 不在 bounds 内时回退为 bounds 中心(避免渲染时跳回 0,0 的情况)
float centerX = pivot.x; float centerX = pivot.x;
@@ -3233,6 +3233,7 @@ public class Mesh2D {
bounds[3] + expand bounds[3] + expand
}; };
} }
public void draw() { public void draw() {
if (!visible) return; if (!visible) return;
if (indices == null || indices.length == 0) return; if (indices == null || indices.length == 0) return;
@@ -3456,17 +3457,26 @@ public class Mesh2D {
*/ */
public String getDrawModeString() { public String getDrawModeString() {
switch (drawMode) { switch (drawMode) {
case POINTS: return "POINTS"; case POINTS:
case LINES: return "LINES"; return "POINTS";
case LINE_STRIP: return "LINE_STRIP"; case LINES:
case TRIANGLES: return "TRIANGLES"; return "LINES";
case TRIANGLE_STRIP: return "TRIANGLE_STRIP"; case LINE_STRIP:
case TRIANGLE_FAN: return "TRIANGLE_FAN"; return "LINE_STRIP";
default: return "UNKNOWN"; case TRIANGLES:
return "TRIANGLES";
case TRIANGLE_STRIP:
return "TRIANGLE_STRIP";
case TRIANGLE_FAN:
return "TRIANGLE_FAN";
default:
return "UNKNOWN";
} }
} }
/** 标记或查询网格顶点是否已经被烘焙到世界坐标 */ /**
* 标记或查询网格顶点是否已经被烘焙到世界坐标
*/
public void setBakedToWorld(boolean baked) { public void setBakedToWorld(boolean baked) {
this.bakedToWorld = baked; this.bakedToWorld = baked;
} }

View File

@@ -4,13 +4,16 @@ import com.chuangzhou.vivid2D.render.model.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.ModelPart;
import org.joml.Vector2f; import org.joml.Vector2f;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* 2D物理系统用于处理模型的物理模拟 * 2D物理系统用于处理模型的物理模拟
* 支持弹簧系统、碰撞检测、重力等物理效果 * 支持弹簧系统、碰撞检测、重力等物理效果
* * <p>
* 调整点: * 调整点:
* - 使用半隐式symplecticEuler 积分,替代之前的纯 Verlet更稳定且直观 * - 使用半隐式symplecticEuler 积分,替代之前的纯 Verlet更稳定且直观
* - 在约束迭代后同步速度(根据位置变化计算),避免约束把位置推回后速度不一致导致僵化 * - 在约束迭代后同步速度(根据位置变化计算),避免约束把位置推回后速度不一致导致僵化
@@ -471,8 +474,7 @@ public class PhysicsSystem {
private void applyToModel(Model2D model) { private void applyToModel(Model2D model) {
for (PhysicsParticle particle : particles.values()) { for (PhysicsParticle particle : particles.values()) {
Object userData = particle.getUserData(); Object userData = particle.getUserData();
if (userData instanceof ModelPart) { if (userData instanceof ModelPart part) {
ModelPart part = (ModelPart) userData;
part.setPosition(particle.getPosition()); part.setPosition(particle.getPosition());
// 可选:根据速度设置旋转 // 可选:根据速度设置旋转
@@ -611,11 +613,8 @@ public class PhysicsSystem {
Vector2f positionDelta = new Vector2f(particle.getPosition()) Vector2f positionDelta = new Vector2f(particle.getPosition())
.sub(particle.getPreviousPosition()); // 现在可以正常使用了 .sub(particle.getPreviousPosition()); // 现在可以正常使用了
float positionDeltaSquared = positionDelta.lengthSquared(); float positionDeltaSquared = positionDelta.lengthSquared();
if (positionDeltaSquared > 0.001f) { // 位置变化阈值,可调整 // 位置变化阈值,可调整
return true; return positionDeltaSquared > 0.001f;
}
return false;
}); });
// 检查是否有活跃的约束 // 检查是否有活跃的约束
@@ -731,31 +730,78 @@ public class PhysicsSystem {
} }
// Getter/Setter 方法 // Getter/Setter 方法
public String getId() { return id; } public String getId() {
public Vector2f getPosition() { return new Vector2f(position); } return id;
}
public Vector2f getPosition() {
return new Vector2f(position);
}
public void setPosition(Vector2f position) { public void setPosition(Vector2f position) {
this.position.set(position); this.position.set(position);
} }
public Vector2f getVelocity() { return new Vector2f(velocity); }
public Vector2f getVelocity() {
return new Vector2f(velocity);
}
public void setVelocity(Vector2f velocity) { public void setVelocity(Vector2f velocity) {
this.velocity.set(velocity); this.velocity.set(velocity);
} }
public Vector2f getAcceleration() { return new Vector2f(acceleration); }
public Vector2f getAcceleration() {
return new Vector2f(acceleration);
}
public float getMass() { public float getMass() {
if (Float.isInfinite(mass)) return Float.POSITIVE_INFINITY; if (Float.isInfinite(mass)) return Float.POSITIVE_INFINITY;
return mass; return mass;
} }
public float getInverseMass() { return inverseMass; }
public float getRadius() { return radius; } public float getInverseMass() {
public void setRadius(float radius) { this.radius = radius; } return inverseMass;
public boolean isMovable() { return movable; } }
public void setMovable(boolean movable) { this.movable = movable; }
public boolean isAffectedByGravity() { return affectedByGravity; } public float getRadius() {
public void setAffectedByGravity(boolean affectedByGravity) { this.affectedByGravity = affectedByGravity; } return radius;
public Object getUserData() { return userData; } }
public void setUserData(Object userData) { this.userData = userData; }
public boolean isAffectedByWind() { return affectedByWind; } public void setRadius(float radius) {
public void setAffectedByWind(boolean affectedByWind) { this.affectedByWind = affectedByWind; } this.radius = radius;
}
public boolean isMovable() {
return movable;
}
public void setMovable(boolean movable) {
this.movable = movable;
}
public boolean isAffectedByGravity() {
return affectedByGravity;
}
public void setAffectedByGravity(boolean affectedByGravity) {
this.affectedByGravity = affectedByGravity;
}
public Object getUserData() {
return userData;
}
public void setUserData(Object userData) {
this.userData = userData;
}
public boolean isAffectedByWind() {
return affectedByWind;
}
public void setAffectedByWind(boolean affectedByWind) {
this.affectedByWind = affectedByWind;
}
} }
/** /**
@@ -814,14 +860,37 @@ public class PhysicsSystem {
} }
// Getter/Setter 方法 // Getter/Setter 方法
public String getId() { return id; } public String getId() {
public PhysicsParticle getParticleA() { return particleA; } return id;
public PhysicsParticle getParticleB() { return particleB; } }
public float getRestLength() { return restLength; }
public float getStiffness() { return stiffness; } public PhysicsParticle getParticleA() {
public float getDamping() { return damping; } return particleA;
public boolean isEnabled() { return enabled; } }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public PhysicsParticle getParticleB() {
return particleB;
}
public float getRestLength() {
return restLength;
}
public float getStiffness() {
return stiffness;
}
public float getDamping() {
return damping;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
} }
/** /**
@@ -829,8 +898,11 @@ public class PhysicsSystem {
*/ */
public interface PhysicsConstraint { public interface PhysicsConstraint {
void apply(float deltaTime); void apply(float deltaTime);
PhysicsParticle getParticle(); PhysicsParticle getParticle();
boolean isEnabled(); boolean isEnabled();
void setEnabled(boolean enabled); void setEnabled(boolean enabled);
} }
@@ -863,13 +935,36 @@ public class PhysicsSystem {
} }
// Getter/Setter 方法 // Getter/Setter 方法
@Override public PhysicsParticle getParticle() { return particle; } @Override
@Override public boolean isEnabled() { return enabled; } public PhysicsParticle getParticle() {
@Override public void setEnabled(boolean enabled) { this.enabled = enabled; } return particle;
public Vector2f getTargetPosition() { return new Vector2f(targetPosition); } }
public void setTargetPosition(Vector2f targetPosition) { this.targetPosition.set(targetPosition); }
public float getStrength() { return strength; } @Override
public void setStrength(float strength) { this.strength = Math.max(0.0f, Math.min(1.0f, strength)); } public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Vector2f getTargetPosition() {
return new Vector2f(targetPosition);
}
public void setTargetPosition(Vector2f targetPosition) {
this.targetPosition.set(targetPosition);
}
public float getStrength() {
return strength;
}
public void setStrength(float strength) {
this.strength = Math.max(0.0f, Math.min(1.0f, strength));
}
} }
/** /**
@@ -902,11 +997,28 @@ public class PhysicsSystem {
} }
// Getter/Setter 方法 // Getter/Setter 方法
@Override public PhysicsParticle getParticle() { return particle; } @Override
@Override public boolean isEnabled() { return enabled; } public PhysicsParticle getParticle() {
@Override public void setEnabled(boolean enabled) { this.enabled = enabled; } return particle;
public PhysicsParticle getTarget() { return target; } }
public float getMaxDistance() { return maxDistance; }
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public PhysicsParticle getTarget() {
return target;
}
public float getMaxDistance() {
return maxDistance;
}
} }
/** /**
@@ -914,9 +1026,13 @@ public class PhysicsSystem {
*/ */
public interface PhysicsCollider { public interface PhysicsCollider {
boolean collidesWith(PhysicsParticle particle); boolean collidesWith(PhysicsParticle particle);
void resolveCollision(PhysicsParticle particle, float deltaTime); void resolveCollision(PhysicsParticle particle, float deltaTime);
String getId(); String getId();
boolean isEnabled(); boolean isEnabled();
void setEnabled(boolean enabled); void setEnabled(boolean enabled);
} }
@@ -973,12 +1089,32 @@ public class PhysicsSystem {
} }
// Getter/Setter 方法 // Getter/Setter 方法
@Override public String getId() { return id; } @Override
@Override public boolean isEnabled() { return enabled; } public String getId() {
@Override public void setEnabled(boolean enabled) { this.enabled = enabled; } return id;
public Vector2f getCenter() { return new Vector2f(center); } }
public void setCenter(Vector2f center) { this.center.set(center); }
public float getRadius() { return radius; } @Override
public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Vector2f getCenter() {
return new Vector2f(center);
}
public void setCenter(Vector2f center) {
this.center.set(center);
}
public float getRadius() {
return radius;
}
} }
/** /**
@@ -1059,13 +1195,36 @@ public class PhysicsSystem {
} }
// Getter/Setter 方法 // Getter/Setter 方法
@Override public String getId() { return id; } @Override
@Override public boolean isEnabled() { return enabled; } public String getId() {
@Override public void setEnabled(boolean enabled) { this.enabled = enabled; } return id;
public Vector2f getCenter() { return new Vector2f(center); } }
public void setCenter(Vector2f center) { this.center.set(center); }
public float getWidth() { return width; } @Override
public float getHeight() { return height; } public boolean isEnabled() {
return enabled;
}
@Override
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Vector2f getCenter() {
return new Vector2f(center);
}
public void setCenter(Vector2f center) {
this.center.set(center);
}
public float getWidth() {
return width;
}
public float getHeight() {
return height;
}
} }
/** /**
@@ -1090,12 +1249,29 @@ public class PhysicsSystem {
} }
// Getter 方法 // Getter 方法
public int getParticleCount() { return particleCount; } public int getParticleCount() {
public int getSpringCount() { return springCount; } return particleCount;
public int getConstraintCount() { return constraintCount; } }
public int getColliderCount() { return colliderCount; }
public float getAverageUpdateTime() { return averageUpdateTime; } public int getSpringCount() {
public int getTotalUpdates() { return totalUpdates; } return springCount;
}
public int getConstraintCount() {
return constraintCount;
}
public int getColliderCount() {
return colliderCount;
}
public float getAverageUpdateTime() {
return averageUpdateTime;
}
public int getTotalUpdates() {
return totalUpdates;
}
@Override @Override
public String toString() { public String toString() {

View File

@@ -1,12 +1,7 @@
package com.chuangzhou.vivid2D.render.model.util; package com.chuangzhou.vivid2D.render.model.util;
import com.chuangzhou.vivid2D.render.systems.RenderSystem; import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import org.lwjgl.opengl.GL; import org.lwjgl.opengl.*;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL30;
import org.lwjgl.stb.STBImage; import org.lwjgl.stb.STBImage;
import org.lwjgl.stb.STBImageWrite; import org.lwjgl.stb.STBImageWrite;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@@ -75,9 +70,17 @@ public class Texture {
this.glFormat = glFormat; this.glFormat = glFormat;
} }
public int getComponents() { return components; } public int getComponents() {
public int getGLInternalFormat() { return glInternalFormat; } return components;
public int getGLFormat() { return glFormat; } }
public int getGLInternalFormat() {
return glInternalFormat;
}
public int getGLFormat() {
return glFormat;
}
} }
public enum TextureType { public enum TextureType {
@@ -95,7 +98,9 @@ public class Texture {
this.glType = glType; this.glType = glType;
} }
public int getGLType() { return glType; } public int getGLType() {
return glType;
}
} }
public enum TextureFilter { public enum TextureFilter {
@@ -112,7 +117,9 @@ public class Texture {
this.glFilter = glFilter; this.glFilter = glFilter;
} }
public int getGLFilter() { return glFilter; } public int getGLFilter() {
return glFilter;
}
} }
public enum TextureWrap { public enum TextureWrap {
@@ -127,7 +134,9 @@ public class Texture {
this.glWrap = glWrap; this.glWrap = glWrap;
} }
public int getGLWrap() { return glWrap; } public int getGLWrap() {
return glWrap;
}
} }
// ==================== STB 图像加载 ==================== // ==================== STB 图像加载 ====================
@@ -244,7 +253,8 @@ public class Texture {
// 恢复原先的 UNPACK_ALIGNMENT // 恢复原先的 UNPACK_ALIGNMENT
try { try {
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, prevUnpack); GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, prevUnpack);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
unbind(); unbind();
} }
} }
@@ -869,10 +879,14 @@ public class Texture {
*/ */
public static TextureFormat getTextureFormat(int components) { public static TextureFormat getTextureFormat(int components) {
switch (components) { switch (components) {
case 1: return TextureFormat.RED; case 1:
case 2: return TextureFormat.RG; return TextureFormat.RED;
case 3: return TextureFormat.RGB; case 2:
case 4: return TextureFormat.RGBA; return TextureFormat.RG;
case 3:
return TextureFormat.RGB;
case 4:
return TextureFormat.RGBA;
default: default:
throw new IllegalArgumentException("Unsupported number of components: " + components); throw new IllegalArgumentException("Unsupported number of components: " + components);
} }
@@ -1071,7 +1085,6 @@ public class Texture {
} }
/** /**
* 从字节数组创建纹理 * 从字节数组创建纹理
*/ */
@@ -1196,13 +1209,20 @@ public class Texture {
*/ */
private String getGLErrorString(int error) { private String getGLErrorString(int error) {
switch (error) { switch (error) {
case GL11.GL_INVALID_ENUM: return "GL_INVALID_ENUM"; case GL11.GL_INVALID_ENUM:
case GL11.GL_INVALID_VALUE: return "GL_INVALID_VALUE"; return "GL_INVALID_ENUM";
case GL11.GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; case GL11.GL_INVALID_VALUE:
case GL11.GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; return "GL_INVALID_VALUE";
case GL11.GL_STACK_OVERFLOW: return "GL_STACK_OVERFLOW"; case GL11.GL_INVALID_OPERATION:
case GL11.GL_STACK_UNDERFLOW: return "GL_STACK_UNDERFLOW"; return "GL_INVALID_OPERATION";
default: return "Unknown Error (0x" + Integer.toHexString(error) + ")"; case GL11.GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
case GL11.GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW";
case GL11.GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW";
default:
return "Unknown Error (0x" + Integer.toHexString(error) + ")";
} }
} }

View File

@@ -26,6 +26,7 @@ import java.util.function.Supplier;
*/ */
public final class RenderSystem { public final class RenderSystem {
private static final Logger logger = LoggerFactory.getLogger(RenderSystem.class); private static final Logger logger = LoggerFactory.getLogger(RenderSystem.class);
private RenderSystem() { /* no instances */ } private RenderSystem() { /* no instances */ }
// ================== 线程管理 ================== // ================== 线程管理 ==================
@@ -657,6 +658,7 @@ public final class RenderSystem {
assertOnRenderThread(); assertOnRenderThread();
GL11.glLineWidth(width); GL11.glLineWidth(width);
} }
public static void drawElements(int mode, int count, int type, long indices) { public static void drawElements(int mode, int count, int type, long indices) {
if (!isOnRenderThread()) { if (!isOnRenderThread()) {
recordRenderCall(() -> _drawElements(mode, count, type, indices)); recordRenderCall(() -> _drawElements(mode, count, type, indices));
@@ -1206,6 +1208,7 @@ public final class RenderSystem {
/** /**
* 设置着色器纹理 * 设置着色器纹理
*
* @param textureUnit 纹理单元 (0, 1, 2, ...) * @param textureUnit 纹理单元 (0, 1, 2, ...)
* @param texture 纹理对象 * @param texture 纹理对象
*/ */
@@ -1247,6 +1250,7 @@ public final class RenderSystem {
/** /**
* 设置着色器纹理 - 接受纹理ID的版本 * 设置着色器纹理 - 接受纹理ID的版本
*
* @param textureUnit 纹理单元 * @param textureUnit 纹理单元
* @param textureId 纹理ID * @param textureId 纹理ID
*/ */
@@ -1433,6 +1437,7 @@ public final class RenderSystem {
return false; return false;
} }
public static void pixelStore(int pname, int param) { public static void pixelStore(int pname, int param) {
if (!isOnRenderThread()) { if (!isOnRenderThread()) {
recordRenderCall(() -> _pixelStore(pname, param)); recordRenderCall(() -> _pixelStore(pname, param));
@@ -1514,6 +1519,7 @@ public final class RenderSystem {
logger.info("Max Texture Units: {}", logger.info("Max Texture Units: {}",
GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS)); GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS));
} }
public static void setupDefaultState() { public static void setupDefaultState() {
beginInitialization(); beginInitialization();
@@ -1559,17 +1565,22 @@ public final class RenderSystem {
} }
logger.error("OpenGL error during {}: {}\n{}", logger.error("OpenGL error during {}: {}\n{}",
operation, getGLErrorString(error), stackTraceBuilder.toString()); operation, getGLErrorString(error), stackTraceBuilder);
} }
} }
private static String getGLErrorString(int error) { private static String getGLErrorString(int error) {
switch (error) { switch (error) {
case GL11.GL_INVALID_ENUM: return "GL_INVALID_ENUM"; case GL11.GL_INVALID_ENUM:
case GL11.GL_INVALID_VALUE: return "GL_INVALID_VALUE"; return "GL_INVALID_ENUM";
case GL11.GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; case GL11.GL_INVALID_VALUE:
case GL11.GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; return "GL_INVALID_VALUE";
default: return "Unknown (0x" + Integer.toHexString(error) + ")"; case GL11.GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION";
case GL11.GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY";
default:
return "Unknown (0x" + Integer.toHexString(error) + ")";
} }
} }

View File

@@ -1,18 +1,20 @@
package com.chuangzhou.vivid2D.render.systems.sources.def; package com.chuangzhou.vivid2D.render.systems.sources.def;
import com.chuangzhou.vivid2D.render.systems.sources.*; import com.chuangzhou.vivid2D.render.systems.sources.CompleteShader;
import com.chuangzhou.vivid2D.render.systems.sources.Shader;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
import org.joml.Vector4f; import org.joml.Vector4f;
/** /**
* 文本着色器 * 文本着色器
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class TextShader implements CompleteShader { public class TextShader implements CompleteShader {
private final VertexShader vertexShader = new VertexShader(); private final VertexShader vertexShader = new VertexShader();
private final FragmentShader fragmentShader = new FragmentShader(); private final FragmentShader fragmentShader = new FragmentShader();
private Vector4f color = new Vector4f(1,1,1,1); private final Vector4f color = new Vector4f(1, 1, 1, 1);
public void setColor(Vector4f color) { public void setColor(Vector4f color) {
this.color.set(color); this.color.set(color);