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

@@ -2,25 +2,30 @@ package com.chuangzhou.vivid2D.render;
import com.chuangzhou.vivid2D.render.model.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.systems.Camera;
import com.chuangzhou.vivid2D.render.systems.buffer.BufferBuilder;
import com.chuangzhou.vivid2D.render.systems.buffer.Tesselator;
import com.chuangzhou.vivid2D.render.model.util.LightSource;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem;
import com.chuangzhou.vivid2D.render.systems.Camera;
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import com.chuangzhou.vivid2D.render.systems.buffer.BufferBuilder;
import com.chuangzhou.vivid2D.render.systems.buffer.Tesselator;
import com.chuangzhou.vivid2D.render.systems.sources.CompleteShader;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
import org.joml.Matrix3f;
import org.joml.Vector2f;
import org.joml.Vector4f;
import org.lwjgl.opengl.*;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -65,6 +70,7 @@ public final class ModelRender {
/**
* 渲染系统初始化状态标志,确保系统只初始化一次
*
* @see #initialize()
* @see #isInitialized()
*/
@@ -73,6 +79,7 @@ public final class ModelRender {
/**
* 视口宽度(像素),定义渲染区域的大小
* 默认值800像素
*
* @see #setViewport(int, int)
*/
static int viewportWidth = 800;
@@ -80,6 +87,7 @@ public final class ModelRender {
/**
* 视口高度(像素),定义渲染区域的大小
* 默认值600像素
*
* @see #setViewport(int, int)
*/
static int viewportHeight = 600;
@@ -87,6 +95,7 @@ public final class ModelRender {
/**
* 清除颜色RGBA用于在每帧开始时清空颜色缓冲区
* 默认值:黑色不透明 (0.0f, 0.0f, 0.0f, 1.0f)
*
* @see RenderSystem#clearColor(float, float, float, float)
*/
private static final Vector4f CLEAR_COLOR = new Vector4f(0.0f, 0.0f, 0.0f, 1.0f);
@@ -95,6 +104,7 @@ public final class ModelRender {
* 深度测试启用标志,控制是否进行深度缓冲测试
* 在2D渲染中通常禁用以提高性能
* 默认值false禁用
*
* @see RenderSystem#enableDepthTest()
* @see RenderSystem#disableDepthTest()
*/
@@ -103,6 +113,7 @@ public final class ModelRender {
/**
* 混合功能启用标志,控制透明度和颜色混合
* 默认值true启用
*
* @see RenderSystem#enableBlend()
* @see RenderSystem#disableBlend()
*/
@@ -119,6 +130,7 @@ public final class ModelRender {
/**
* 默认着色器程序,用于大多数模型的渲染
* 包含基础的光照、纹理和变换功能
*
* @see #compileDefaultShader()
*/
private static ShaderProgram defaultProgram = null;
@@ -127,6 +139,7 @@ public final class ModelRender {
* 网格GPU资源缓存管理已上传到GPU的网格数据
* 键Mesh2D对象
* 值对应的OpenGL资源VAO、VBO、EBO
*
* @see MeshGLResources
*/
private static final Map<Mesh2D, MeshGLResources> meshResources = new HashMap<>();
@@ -141,6 +154,7 @@ public final class ModelRender {
/**
* 默认白色纹理ID当模型没有指定纹理时使用
* 这是一个1x1的纯白色纹理确保模型有基本的颜色显示
*
* @see #createDefaultTexture()
*/
private static int defaultTextureId = 0;
@@ -189,7 +203,7 @@ public final class ModelRender {
*/
private static final Camera camera = new Camera();
// ================== 字体管理 ==================
// ================== 字体管理 ==================
private static TextRenderer defaultTextRenderer = null;
private static final int FONT_BITMAP_WIDTH = 512;
private static final int FONT_BITMAP_HEIGHT = 512;
@@ -290,9 +304,18 @@ public final class ModelRender {
boolean initialized = false;
void dispose() {
if (ebo != 0) { GL15.glDeleteBuffers(ebo); ebo = 0; }
if (vbo != 0) { GL15.glDeleteBuffers(vbo); vbo = 0; }
if (vao != 0) { GL30.glDeleteVertexArrays(vao); vao = 0; }
if (ebo != 0) {
GL15.glDeleteBuffers(ebo);
ebo = 0;
}
if (vbo != 0) {
GL15.glDeleteBuffers(vbo);
vbo = 0;
}
if (vao != 0) {
GL30.glDeleteVertexArrays(vao);
vao = 0;
}
initialized = false;
}
}
@@ -722,12 +745,13 @@ public final class ModelRender {
}
}
// 恢复原始颜色
setUniformVec4Internal(defaultProgram, "uColor", new Vector4f(1,1,1,1));
setUniformVec4Internal(defaultProgram, "uColor", new Vector4f(1, 1, 1, 1));
}
/**
* 绘制简洁的灯泡形状
* @param position 灯泡位置
*
* @param position 灯泡位置
* @param intensity 光源强度,用于控制灯泡大小
*/
private static void drawLightBulb(Vector2f position, float intensity) {
@@ -868,16 +892,14 @@ public final class ModelRender {
RenderSystem.checkGLError("before_render_collider_" + enabledColliders);
if (collider instanceof PhysicsSystem.CircleCollider) {
PhysicsSystem.CircleCollider c = (PhysicsSystem.CircleCollider) collider;
if (collider instanceof PhysicsSystem.CircleCollider c) {
if (c.getCenter() != null && c.getRadius() > 0) {
drawCircleColliderWire(c.getCenter(), c.getRadius());
enabledColliders++;
} else {
logger.warn("Invalid CircleCollider: center={}, radius={}", c.getCenter(), c.getRadius());
}
} else if (collider instanceof PhysicsSystem.RectangleCollider) {
PhysicsSystem.RectangleCollider r = (PhysicsSystem.RectangleCollider) collider;
} else if (collider instanceof PhysicsSystem.RectangleCollider r) {
if (r.getCenter() != null && r.getWidth() > 0 && r.getHeight() > 0) {
drawRectangleColliderWire(r.getCenter(), r.getWidth(), r.getHeight());
enabledColliders++;
@@ -977,17 +999,25 @@ public final class ModelRender {
ModelPart.BlendMode bm = part.getBlendMode();
if (bm != null) {
switch (bm) {
case ADDITIVE: blend = 1; break;
case MULTIPLY: blend = 2; break;
case SCREEN: blend = 3; break;
case NORMAL: default: blend = 0;
case ADDITIVE:
blend = 1;
break;
case MULTIPLY:
blend = 2;
break;
case SCREEN:
blend = 3;
break;
case NORMAL:
default:
blend = 0;
}
} else {
blend = 0;
}
setUniformIntInternal(sp, "uBlendMode", blend);
// 这里保留为白色,若需要部件 tint 请替换为 part 的 color 属性
setUniformVec4Internal(sp, "uColor", new Vector4f(1,1,1,1));
setUniformVec4Internal(sp, "uColor", new Vector4f(1, 1, 1, 1));
}
public static TextRenderer getTextRenderer() {
@@ -1009,9 +1039,10 @@ public final class ModelRender {
/**
* 渲染文字
* @param text 文字内容
* @param x 世界坐标 X
* @param y 世界坐标 Y ,反转的
*
* @param text 文字内容
* @param x 世界坐标 X
* @param y 世界坐标 Y ,反转的
* @param color RGBA 颜色
*/
public static void renderText(String text, float x, float y, Vector4f color) {
@@ -1025,6 +1056,7 @@ public final class ModelRender {
/**
* 获取默认摄像机与当前摄像机之间的偏移量
*
* @return Vector2f 偏移向量 (dx, dy)
*/
public static Vector2f getCameraOffset() {
@@ -1033,15 +1065,16 @@ public final class ModelRender {
float zoom = camera.getZoom();
Vector2f pos = camera.getPosition();
float tx = -1.0f - (2.0f * zoom * pos.x / width);
float ty = 1.0f + (2.0f * zoom * pos.y / height);
float ty = 1.0f + (2.0f * zoom * pos.y / height);
float tx0 = -1.0f;
float ty0 = 1.0f;
float ty0 = 1.0f;
float offsetX = tx - tx0;
float offsetY = ty - ty0;
offsetX = -offsetX * width / 2.0f / zoom;
offsetY = offsetY * height / 2.0f / zoom;
return new Vector2f(offsetX, offsetY);
}
public static void setViewport(int width, int height) {
viewportWidth = Math.max(1, width);
viewportHeight = Math.max(1, height);
@@ -1049,7 +1082,12 @@ public final class ModelRender {
}
// ================== 辅助:外部获取状态 ==================
public static boolean isInitialized() { return initialized; }
public static int getLoadedMeshCount() { return meshResources.size(); }
public static boolean isInitialized() {
return initialized;
}
public static int getLoadedMeshCount() {
return meshResources.size();
}
}

View File

@@ -11,11 +11,11 @@ import org.lwjgl.opengl.GL11;
/**
* 现代化选择框渲染器(性能优化版)
* 主要优化点:
* 1) 复用 Tesselator 单例 BufferBuilder减少频繁的 GPU 资源创建/销毁
* 2) 批量提交顶点:把同一 primitiveLINES / TRIANGLES / LINE_LOOP与同一颜色的顶点尽量合并到一次 begin/end
* 3) 手柄使用实心矩形(两三角形)批量绘制,保持美观且高效
* 4) 增加轻微外发光(透明大边框)和阴影感以达到“现代”外观
*
* 1) 复用 Tesselator 单例 BufferBuilder减少频繁的 GPU 资源创建/销毁
* 2) 批量提交顶点:把同一 primitiveLINES / TRIANGLES / LINE_LOOP与同一颜色的顶点尽量合并到一次 begin/end
* 3) 手柄使用实心矩形(两三角形)批量绘制,保持美观且高效
* 4) 增加轻微外发光(透明大边框)和阴影感以达到“现代”外观
* <p>
* 注意:本类依赖你工程中已有的 RenderSystem/Tesselator/BufferBuilder/BufferUploader 实现。
*/
public class MultiSelectionBoxRenderer {

View File

@@ -11,9 +11,10 @@ import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
public interface ModelClickListener {
/**
* 当点击模型时触发
* @param mesh 被点击的网格,如果点击在空白处则为 null
* @param modelX 模型坐标系中的 X 坐标
* @param modelY 模型坐标系中的 Y 坐标
*
* @param mesh 被点击的网格,如果点击在空白处则为 null
* @param modelX 模型坐标系中的 X 坐标
* @param modelY 模型坐标系中的 Y 坐标
* @param screenX 屏幕坐标系中的 X 坐标
* @param screenY 屏幕坐标系中的 Y 坐标
*/
@@ -21,22 +22,32 @@ public interface ModelClickListener {
/**
* 当鼠标在模型上移动时触发
* @param mesh 鼠标下方的网格,如果不在任何网格上则为 null
* @param modelX 模型坐标系中的 X 坐标
* @param modelY 模型坐标系中的 Y 坐标
*
* @param mesh 鼠标下方的网格,如果不在任何网格上则为 null
* @param modelX 模型坐标系中的 X 坐标
* @param modelY 模型坐标系中的 Y 坐标
* @param screenX 屏幕坐标系中的 X 坐标
* @param screenY 屏幕坐标系中的 Y 坐标
*/
default void onModelHover(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {}
default void onLiquifyModeExited(){};
default void onModelHover(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {
}
default void onLiquifyModeEntered(Mesh2D targetMesh, ModelPart liquifyTargetPart){};
default void onLiquifyModeExited() {
}
default void onSecondaryVertexModeEntered(Mesh2D secondaryVertexTargetMesh){};
default void onLiquifyModeEntered(Mesh2D targetMesh, ModelPart liquifyTargetPart) {
}
default void onSecondaryVertexModeExited(){};
default void onSecondaryVertexModeEntered(Mesh2D secondaryVertexTargetMesh) {
}
default void onPuppetModeEntered(Mesh2D puppetTargetMesh){};
default void onSecondaryVertexModeExited() {
}
default void onPuppetModeEntered(Mesh2D puppetTargetMesh) {
}
default void onPuppetModeExited() {
}
default void onPuppetModeExited(){};
}

View File

@@ -16,11 +16,15 @@ import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
@@ -30,13 +34,13 @@ import java.util.concurrent.Callable;
/**
* ModelLayerPanel完整实现
*
* <p>
* - 列表显示“从上到下”的图层listModel[0] 为最上层)
* - 在任何修改后都会把 model.parts 同步为列表的反序(保证渲染顺序与 UI 一致)
* - 支持添加空层 / 从文件创建带贴图的层(在有 renderPanel 时在 GL 线程使用 Texture.createFromFile
* - 支持为选中部件绑定贴图、创建透明图层
* - 支持拖拽重排、上下按钮移动,并在重排后正确恢复选中与不触发滑块事件
*
* <p>
* 使用:
* new ModelLayerPanel(model, optionalModelRenderPanel)
*/
@@ -242,7 +246,8 @@ public class ModelLayerPanel extends JPanel {
if (partMap != null) {
partMap.put(uniqueName, part);
}
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
part.setVisible(layerInfo.visible);
@@ -293,7 +298,8 @@ public class ModelLayerPanel extends JPanel {
// 强制尝试上传/初始化(若纹理对象需要)
try {
tryCallTextureUpload(texture);
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
// 标记模型需要更新
model.markNeedsUpdate();
@@ -306,10 +312,12 @@ public class ModelLayerPanel extends JPanel {
SwingUtilities.invokeLater(() -> {
try {
reloadFromModel();
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
try {
if (renderPanel != null) renderPanel.repaint();
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
});
} else {
System.err.println("创建纹理失败: " + uniqueName);
@@ -505,7 +513,8 @@ public class ModelLayerPanel extends JPanel {
f.setAccessible(true);
Object v = f.get(sel);
if (v instanceof Float) op = (Float) v;
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
}
int val = Math.round(op * 100);
@@ -604,7 +613,8 @@ public class ModelLayerPanel extends JPanel {
Field f = sel.getClass().getDeclaredField("opacity");
f.setAccessible(true);
f.setFloat(sel, val / 100.0f);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
}
if (model != null) model.markNeedsUpdate();
layerList.repaint();
@@ -711,7 +721,8 @@ public class ModelLayerPanel extends JPanel {
w = Math.max(1, Integer.parseInt(sp[0].trim()));
h = Math.max(1, Integer.parseInt(sp[1].trim()));
}
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
ModelPart part = model.createPart(name);
@@ -753,20 +764,23 @@ public class ModelLayerPanel extends JPanel {
File f = chooser.getSelectedFile();
try {
BufferedImage img = null;
try { img = ImageIO.read(f); } catch (Exception ignored) {}
try {
img = ImageIO.read(f);
} catch (Exception ignored) {
}
// 获取第一个 mesh
Mesh2D targetMesh = null;
try {
Method getMeshes = sel.getClass().getMethod("getMeshes");
Object list = getMeshes.invoke(sel);
if (list instanceof List) {
List<?> meshes = (List<?>) list;
if (list instanceof List<?> meshes) {
if (!meshes.isEmpty() && meshes.get(0) instanceof Mesh2D) {
targetMesh = (Mesh2D) meshes.get(0);
}
}
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
if (targetMesh == null) {
if (img == null) {
@@ -828,12 +842,12 @@ public class ModelLayerPanel extends JPanel {
try {
Method m = Mesh2D.class.getMethod("createQuad", String.class, float.class, float.class);
Object o = m.invoke(null, meshName, w, h);
if (o instanceof Mesh2D) {
Mesh2D mesh = (Mesh2D) o;
if (o instanceof Mesh2D mesh) {
// 对基础四边形进行细分以增加顶点密度
return subdivideMeshForLiquify(mesh, 3); // 3级细分
}
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
try {
// 创建高密度网格(细分网格)
@@ -914,8 +928,7 @@ public class ModelLayerPanel extends JPanel {
if (cons != null) {
cons.setAccessible(true);
Object meshObj = cons.newInstance(name, vertices, uvs, indices);
if (meshObj instanceof Mesh2D) {
Mesh2D mesh = (Mesh2D) meshObj;
if (meshObj instanceof Mesh2D mesh) {
// 设置合适的pivot中心点
mesh.setPivot(0, 0);
@@ -1074,11 +1087,12 @@ public class ModelLayerPanel extends JPanel {
if (renderPanel == null) throw new IllegalStateException("需要 renderPanel 才能在 GL 上下文创建纹理");
try {
return renderPanel.executeInGLContext((Callable<Texture>) () -> {
return renderPanel.executeInGLContext(() -> {
// 静态工厂尝试
try {
Method factory = findStaticMethod(Texture.class, "createFromBufferedImage", BufferedImage.class);
if (factory == null) factory = findStaticMethod(Texture.class, "createFromImage", BufferedImage.class);
if (factory == null)
factory = findStaticMethod(Texture.class, "createFromImage", BufferedImage.class);
if (factory != null) {
Object texObj = factory.invoke(null, img);
if (texObj instanceof Texture) {
@@ -1086,7 +1100,8 @@ public class ModelLayerPanel extends JPanel {
return (Texture) texObj;
}
}
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
// 构造 ByteBuffer 并尝试构造器
try {
@@ -1123,15 +1138,20 @@ public class ModelLayerPanel extends JPanel {
}
}
}
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
if (formatEnum != null) {
try {
texObj = suit.newInstance(texName, w, h, formatEnum, buf);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
}
}
if (texObj == null) {
try { texObj = suit.newInstance(texName, img.getWidth(), img.getHeight(), buf); } catch (Exception ignored) {}
try {
texObj = suit.newInstance(texName, img.getWidth(), img.getHeight(), buf);
} catch (Exception ignored) {
}
}
if (texObj instanceof Texture) {
tryCallTextureUpload((Texture) texObj);
@@ -1185,13 +1205,20 @@ public class ModelLayerPanel extends JPanel {
}
}
}
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
if (formatEnum != null) {
try { texObj = suit.newInstance(texName, w, h, formatEnum, buf); } catch (Throwable ignored) {}
try {
texObj = suit.newInstance(texName, w, h, formatEnum, buf);
} catch (Throwable ignored) {
}
}
}
if (texObj == null) {
try { texObj = suit.newInstance(texName, w, h, buf); } catch (Throwable ignored) {}
try {
texObj = suit.newInstance(texName, w, h, buf);
} catch (Throwable ignored) {
}
}
if (texObj instanceof Texture) return (Texture) texObj;
}
@@ -1245,12 +1272,14 @@ public class ModelLayerPanel extends JPanel {
try {
Method m = cls.getMethod(name, param);
if (Modifier.isStatic(m.getModifiers())) return m;
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
try {
Method m = cls.getDeclaredMethod(name, param);
m.setAccessible(true);
if (Modifier.isStatic(m.getModifiers())) return m;
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
return null;
}
@@ -1277,7 +1306,8 @@ public class ModelLayerPanel extends JPanel {
model.setRootPart(null);
}
}
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
model.markNeedsUpdate();
reloadFromModel();
} catch (Exception ex) {
@@ -1449,9 +1479,7 @@ public class ModelLayerPanel extends JPanel {
Field partsField = model.getClass().getDeclaredField("parts");
partsField.setAccessible(true);
Object old = partsField.get(model);
if (old instanceof java.util.List) {
@SuppressWarnings("rawtypes")
java.util.List rawList = (java.util.List) old;
if (old instanceof @SuppressWarnings("rawtypes")List rawList) {
rawList.clear();
rawList.addAll(newParts);
} else {
@@ -1465,9 +1493,9 @@ public class ModelLayerPanel extends JPanel {
// ============== 列表渲染/拖拽辅助 ==============
private class LayerCellRenderer extends JPanel implements ListCellRenderer<ModelPart> {
private JCheckBox visibleBox = new JCheckBox();
private JLabel nameLabel = new JLabel();
private JLabel opacityLabel = new JLabel();
private final JCheckBox visibleBox = new JCheckBox();
private final JLabel nameLabel = new JLabel();
private final JLabel opacityLabel = new JLabel();
LayerCellRenderer() {
setLayout(new BorderLayout(6, 6));
@@ -1543,7 +1571,9 @@ public class ModelLayerPanel extends JPanel {
}
@Override
public int getSourceActions(JComponent c) { return MOVE; }
public int getSourceActions(JComponent c) {
return MOVE;
}
@Override
public boolean canImport(TransferSupport support) {
@@ -1638,23 +1668,25 @@ public class ModelLayerPanel extends JPanel {
try {
Field fx = p.getClass().getDeclaredField("pivotX");
Field fy = p.getClass().getDeclaredField("pivotY");
fx.setAccessible(true); fy.setAccessible(true);
fx.setAccessible(true);
fy.setAccessible(true);
px = ((Number) fx.get(p)).floatValue();
py = ((Number) fy.get(p)).floatValue();
} catch (Exception ignored2) {}
} catch (Exception ignored2) {
}
}
try {
Method vm = p.getClass().getMethod("isVisible");
visible = (Boolean) vm.invoke(p);
} catch (Exception ignored) {}
System.out.println(String.format("Part[%d] name=%s visible=%s pivot=(%.1f, %.1f)", i, name, visible, px, py));
} catch (Exception ignored) {
}
System.out.printf("Part[%d] name=%s visible=%s pivot=(%.1f, %.1f)%n", i, name, visible, px, py);
// meshes
try {
Method gmsh = p.getClass().getMethod("getMeshes");
Object list = gmsh.invoke(p);
if (list instanceof List) {
List<?> meshes = (List<?>) list;
if (list instanceof List<?> meshes) {
System.out.println(" meshes count = " + meshes.size());
for (int m = 0; m < meshes.size(); m++) {
Object mesh = meshes.get(m);
@@ -1667,7 +1699,8 @@ public class ModelLayerPanel extends JPanel {
Field f = mesh.getClass().getDeclaredField("texture");
f.setAccessible(true);
tex = f.get(mesh);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
}
System.out.println(" mesh[" + m + "] texture = " + (tex == null ? "null" : tex.getClass().getSimpleName()));
}

View File

@@ -14,28 +14,27 @@ import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import com.chuangzhou.vivid2D.test.TestModelGLPanel;
import org.joml.Matrix3f;
import org.joml.Vector2f;
import org.lwjgl.glfw.*;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.event.*;
import java.lang.reflect.Method;
import java.nio.IntBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.locks.LockSupport;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.util.*;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.List;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
/**
* vivid2D 模型的 Java 渲染面板
@@ -43,11 +42,10 @@ import java.util.concurrent.atomic.AtomicReference;
* <p>该类提供了 vivid2D 模型在 Java 环境下的图形渲染功能,
* 包含基本的 2D 图形绘制、模型显示和交互操作。</p>
*
*
* @author tzdwindows 7
* @version 1.1
* @since 2025-10-13
* @see TestModelGLPanel
* @since 2025-10-13
*/
public class ModelRenderPanel extends JPanel {
private static final Logger logger = LoggerFactory.getLogger(ModelRenderPanel.class);
@@ -82,6 +80,7 @@ public class ModelRenderPanel extends JPanel {
private volatile float dragStartX, dragStartY;
private volatile float partStartX, partStartY;
private volatile boolean isDragging = false;
private enum DragMode {
NONE, // 无拖拽
MOVE, // 移动部件
@@ -110,8 +109,8 @@ public class ModelRenderPanel extends JPanel {
public static final float BORDER_THICKNESS = 6.0f;
public static final float CORNER_SIZE = 12.0f;
private volatile float partInitialScaleX = 1.0f;
private volatile float partInitialScaleY = 1.0f;
private final float partInitialScaleX = 1.0f;
private final float partInitialScaleY = 1.0f;
private volatile float displayScale = 1.0f; // 当前可视缩放(用于检测阈值/角点等)
private volatile float targetScale = 1.0f; // 目标缩放(鼠标滚轮/程序改变时设置)
@@ -121,23 +120,23 @@ public class ModelRenderPanel extends JPanel {
private static final float ZOOM_MAX = 8.0f;
private volatile boolean shiftDuringDrag = false;
private volatile float rotationStartAngle = 0.0f;
private volatile float partInitialRotation = 0.0f;
private volatile Vector2f rotationCenter = new Vector2f();
private final float partInitialRotation = 0.0f;
private final Vector2f rotationCenter = new Vector2f();
private static final float ROTATION_HANDLE_DISTANCE = 30.0f;
private OperationHistoryManager historyManager;
// 新增:操作历史管理器
private OperationHistoryGlobal operationHistory;
private final OperationHistoryGlobal operationHistory;
// 新增:拖拽操作的状态记录字段
private Map<ModelPart, Vector2f> dragStartPositions = new HashMap<>();
private Map<ModelPart, Vector2f> dragStartScales = new HashMap<>();
private Map<ModelPart, Float> dragStartRotations = new HashMap<>();
private Map<ModelPart, Vector2f> dragStartPivots = new HashMap<>();
private final Map<ModelPart, Vector2f> dragStartPositions = new HashMap<>();
private final Map<ModelPart, Vector2f> dragStartScales = new HashMap<>();
private final Map<ModelPart, Float> dragStartRotations = new HashMap<>();
private final Map<ModelPart, Vector2f> dragStartPivots = new HashMap<>();
// 新增:鼠标手势相关字段
private volatile Cursor currentCursor = Cursor.getDefaultCursor();
private volatile DragMode hoverDragMode = DragMode.NONE;
private final DragMode hoverDragMode = DragMode.NONE;
private volatile boolean isOverSelection = false;
// ================== 摄像机控制相关字段 ==================
@@ -1036,7 +1035,7 @@ public class ModelRenderPanel extends JPanel {
* 退出液化模式
*/
private void exitLiquifyMode() {
executeInGLContext(()->{
executeInGLContext(() -> {
liquifyMode = false;
if (liquifyTargetPart != null) {
liquifyTargetPart.setStartLiquefy(false);
@@ -1112,7 +1111,7 @@ public class ModelRenderPanel extends JPanel {
// 绘制圆圈
int center = size / 2;
int radius = (int)(liquifyBrushSize * 0.1f); // 根据画笔大小缩放光标
int radius = (int) (liquifyBrushSize * 0.1f); // 根据画笔大小缩放光标
// 外圈
g2d.setColor(Color.RED);
@@ -3342,7 +3341,10 @@ public class ModelRenderPanel extends JPanel {
// 确保缓冲区大小匹配(可能在 resize 后需要重建)
if (pixelBuffer == null || pixelInts == null || pixelInts.length != pixelCount) {
if (pixelBuffer != null) {
try { MemoryUtil.memFree(pixelBuffer); } catch (Throwable ignored) {}
try {
MemoryUtil.memFree(pixelBuffer);
} catch (Throwable ignored) {
}
}
pixelBuffer = MemoryUtil.memAlloc(pixelCount * 4);
pixelBuffer.order(ByteOrder.nativeOrder());
@@ -3431,6 +3433,7 @@ public class ModelRenderPanel extends JPanel {
/**
* 在 GL 上下文线程上异步执行任务
*
* @param task 要在 GL 上下文线程中执行的任务
* @return CompletableFuture 用于获取任务执行结果
*/
@@ -3464,6 +3467,7 @@ public class ModelRenderPanel extends JPanel {
/**
* 在 GL 上下文线程上异步执行任务并返回结果
*
* @param task 要在 GL 上下文线程中执行的有返回值的任务
* @return CompletableFuture 用于获取任务执行结果
*/
@@ -3495,6 +3499,7 @@ public class ModelRenderPanel extends JPanel {
/**
* 同步在 GL 上下文线程上执行任务(会阻塞当前线程直到任务完成)
*
* @param task 要在 GL 上下文线程中执行的任务
* @throws Exception 如果任务执行出错
*/
@@ -3509,6 +3514,7 @@ public class ModelRenderPanel extends JPanel {
/**
* 同步在 GL 上下文线程上执行任务并返回结果(会阻塞当前线程直到任务完成)
*
* @param task 要在 GL 上下文线程中执行的有返回值的任务
* @return 任务执行结果
* @throws Exception 如果任务执行出错或超时
@@ -3541,7 +3547,7 @@ public class ModelRenderPanel extends JPanel {
/**
* 重新设置面板大小
*
* <p>
* 说明:当 Swing 面板被放大时,需要同时调整离屏 GLFW 窗口像素大小、GL 视口以及重分配像素读取缓冲,
* 否则将把较小分辨率的图像拉伸到更大面板上导致模糊。
*/
@@ -3637,7 +3643,8 @@ public class ModelRenderPanel extends JPanel {
if (windowId != 0) {
try {
GLFW.glfwDestroyWindow(windowId);
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
windowId = 0;
}
@@ -3654,7 +3661,8 @@ public class ModelRenderPanel extends JPanel {
// 终止 GLFW注意如果应用中还有其他 GLFW 窗口,这里会影响它们)
try {
GLFW.glfwTerminate();
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
logger.info("OpenGL 资源已清理");
}

View File

@@ -1,25 +1,25 @@
package com.chuangzhou.vivid2D.render.awt;
import com.chuangzhou.vivid2D.render.awt.util.OperationHistoryGlobal;
import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.model.ModelEvent;
import com.chuangzhou.vivid2D.render.model.ModelPart;
import org.joml.Vector2f;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author tzdwindows 7
*/
public class TransformPanel extends JPanel implements ModelEvent {
private ModelRenderPanel renderPanel;
private List<ModelPart> selectedParts = new ArrayList<>();
private final ModelRenderPanel renderPanel;
private final List<ModelPart> selectedParts = new ArrayList<>();
private boolean isMultiSelection = false;
// 位置控制
@@ -47,7 +47,7 @@ public class TransformPanel extends JPanel implements ModelEvent {
private boolean updatingUI = false; // 防止UI更新时触发事件
private javax.swing.Timer transformTimer; // 用于延迟处理变换输入
private OperationHistoryGlobal operationHistory;
private final OperationHistoryGlobal operationHistory;
public TransformPanel(ModelRenderPanel renderPanel) {
this.renderPanel = renderPanel;

View File

@@ -704,16 +704,14 @@ public class OperationHistoryGlobal {
}
private void handleBatchTransformRecord(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Object[] oldValues = (Object[]) params[1];
Object[] newValues = (Object[]) params[2];
}
}
private void executeBatchTransform(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Object[] newValues = (Object[]) params[2];
// 应用新的变换值
@@ -734,8 +732,7 @@ public class OperationHistoryGlobal {
}
private void undoBatchTransform(Object... params) {
if (params.length >= 2 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 2 && params[0] instanceof ModelPart part) {
Object[] oldValues = (Object[]) params[1];
// 恢复旧的变换值
@@ -814,8 +811,7 @@ public class OperationHistoryGlobal {
// 从参数中获取当前位置参数索引从2开始
int paramIndex = 2;
for (ModelPart part : parts) {
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f) {
Vector2f targetPosition = (Vector2f) params[paramIndex];
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f targetPosition) {
part.setPosition(targetPosition.x, targetPosition.y);
paramIndex++;
}
@@ -830,8 +826,7 @@ public class OperationHistoryGlobal {
// 从参数中获取当前缩放参数索引从2开始
int paramIndex = 2;
for (ModelPart part : parts) {
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f) {
Vector2f targetScale = (Vector2f) params[paramIndex];
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f targetScale) {
part.setScale(targetScale.x, targetScale.y);
paramIndex++;
}
@@ -862,8 +857,7 @@ public class OperationHistoryGlobal {
// 从参数中获取当前中心点参数索引从2开始
int paramIndex = 2;
for (ModelPart part : parts) {
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f) {
Vector2f targetPivot = (Vector2f) params[paramIndex];
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f targetPivot) {
part.setPivot(targetPivot.x, targetPivot.y);
paramIndex++;
}
@@ -883,7 +877,7 @@ public class OperationHistoryGlobal {
part.setPosition(startPosition.x, startPosition.y);
}
}
// System.out.println("撤回拖拽结束操作");
// System.out.println("撤回拖拽结束操作");
}
}
@@ -936,8 +930,7 @@ public class OperationHistoryGlobal {
// ============ 具体操作处理方法 ============
private void handlePositionRecord(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Vector2f oldPosition = (Vector2f) params[1];
Vector2f newPosition = (Vector2f) params[2];
@@ -948,24 +941,21 @@ public class OperationHistoryGlobal {
}
private void executePositionChange(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Vector2f newPosition = (Vector2f) params[2];
part.setPosition(newPosition.x, newPosition.y);
}
}
private void undoPositionChange(Object... params) {
if (params.length >= 2 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 2 && params[0] instanceof ModelPart part) {
Vector2f oldPosition = (Vector2f) params[1];
part.setPosition(oldPosition.x, oldPosition.y);
}
}
private void handleRotationRecord(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
float oldRotation = (Float) params[1];
float newRotation = (Float) params[2];
@@ -975,24 +965,21 @@ public class OperationHistoryGlobal {
}
private void executeRotationChange(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
float newRotation = (Float) params[2];
part.setRotation(newRotation);
}
}
private void undoRotationChange(Object... params) {
if (params.length >= 2 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 2 && params[0] instanceof ModelPart part) {
float oldRotation = (Float) params[1];
part.setRotation(oldRotation);
}
}
private void handleScaleRecord(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Vector2f oldScale = (Vector2f) params[1];
Vector2f newScale = (Vector2f) params[2];
@@ -1002,24 +989,21 @@ public class OperationHistoryGlobal {
}
private void executeScaleChange(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Vector2f newScale = (Vector2f) params[2];
part.setScale(newScale.x, newScale.y);
}
}
private void undoScaleChange(Object... params) {
if (params.length >= 2 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 2 && params[0] instanceof ModelPart part) {
Vector2f oldScale = (Vector2f) params[1];
part.setScale(oldScale.x, oldScale.y);
}
}
private void handleOpacityRecord(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
float oldOpacity = (Float) params[1];
float newOpacity = (Float) params[2];
@@ -1029,24 +1013,21 @@ public class OperationHistoryGlobal {
}
private void executeOpacityChange(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
float newOpacity = (Float) params[2];
part.setOpacity(newOpacity);
}
}
private void undoOpacityChange(Object... params) {
if (params.length >= 2 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 2 && params[0] instanceof ModelPart part) {
float oldOpacity = (Float) params[1];
part.setOpacity(oldOpacity);
}
}
private void handleVisibleRecord(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
boolean oldVisible = (Boolean) params[1];
boolean newVisible = (Boolean) params[2];
@@ -1056,24 +1037,21 @@ public class OperationHistoryGlobal {
}
private void executeVisibleChange(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
boolean newVisible = (Boolean) params[2];
part.setVisible(newVisible);
}
}
private void undoVisibleChange(Object... params) {
if (params.length >= 2 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 2 && params[0] instanceof ModelPart part) {
boolean oldVisible = (Boolean) params[1];
part.setVisible(oldVisible);
}
}
private void handlePivotRecord(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Vector2f oldPivot = (Vector2f) params[1];
Vector2f newPivot = (Vector2f) params[2];
@@ -1083,16 +1061,14 @@ public class OperationHistoryGlobal {
}
private void executePivotChange(Object... params) {
if (params.length >= 3 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 3 && params[0] instanceof ModelPart part) {
Vector2f newPivot = (Vector2f) params[2];
part.setPivot(newPivot.x, newPivot.y);
}
}
private void undoPivotChange(Object... params) {
if (params.length >= 2 && params[0] instanceof ModelPart) {
ModelPart part = (ModelPart) params[0];
if (params.length >= 2 && params[0] instanceof ModelPart part) {
Vector2f oldPivot = (Vector2f) params[1];
part.setPivot(oldPivot.x, oldPivot.y);
}
@@ -1459,10 +1435,21 @@ public class OperationHistoryGlobal {
this.canRedo = canRedo;
}
public int getRegisteredOperationCount() { return registeredOperationCount; }
public int getHistorySize() { return historySize; }
public boolean canUndo() { return canUndo; }
public boolean canRedo() { return canRedo; }
public int getRegisteredOperationCount() {
return registeredOperationCount;
}
public int getHistorySize() {
return historySize;
}
public boolean canUndo() {
return canUndo;
}
public boolean canRedo() {
return canRedo;
}
@Override
public String toString() {

View File

@@ -1,14 +1,17 @@
package com.chuangzhou.vivid2D.render.awt.util;
import java.util.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
/**
* 操作记录管理器
* 负责管理操作的撤回和重做
*
* @author tzdwindows 7
*/
public class OperationHistoryManager {
private static OperationHistoryManager instance = new OperationHistoryManager();
private static final OperationHistoryManager instance = new OperationHistoryManager();
// 操作记录栈
private final LinkedList<OperationRecord> undoStack;
private final LinkedList<OperationRecord> redoStack;
@@ -35,6 +38,7 @@ public class OperationHistoryManager {
/**
* 获取操作记录管理器实例
*
* @return 操作记录管理器实例
*/
public static OperationHistoryManager getInstance() {
@@ -43,8 +47,9 @@ public class OperationHistoryManager {
/**
* 注册操作记录器
*
* @param operationType 操作类型标识
* @param recorder 操作记录器
* @param recorder 操作记录器
*/
public void registerRecorder(String operationType, OperationRecorder recorder) {
recorderMap.put(operationType, recorder);
@@ -52,8 +57,9 @@ public class OperationHistoryManager {
/**
* 记录操作
*
* @param operationType 操作类型
* @param params 操作参数
* @param params 操作参数
*/
public void recordOperation(String operationType, Object... params) {
if (!enabled) return;
@@ -203,9 +209,20 @@ public class OperationHistoryManager {
this.timestamp = System.currentTimeMillis();
}
public String getOperationType() { return operationType; }
public Object[] getParams() { return params; }
public String getDescription() { return description; }
public long getTimestamp() { return timestamp; }
public String getOperationType() {
return operationType;
}
public Object[] getParams() {
return params;
}
public String getDescription() {
return description;
}
public long getTimestamp() {
return timestamp;
}
}
}

View File

@@ -8,9 +8,10 @@ public interface OperationListener {
/**
* 操作事件回调
*
* @param operationType 操作类型
* @param action 动作类型record, execute, undo, redo, clear
* @param params 操作参数
* @param action 动作类型record, execute, undo, redo, clear
* @param params 操作参数
*/
void onOperationEvent(String operationType, String action, Object... params);
}

View File

@@ -3,24 +3,28 @@ package com.chuangzhou.vivid2D.render.awt.util;
/**
* 操作记录接口
* 用于注册需要支持撤回/重做的操作
*
* @author tzdwindows 7
*/
public interface OperationRecorder {
/**
* 执行操作(用于重做)
*
* @param params 操作参数
*/
void execute(Object... params);
/**
* 撤销操作
*
* @param params 操作参数
*/
void undo(Object... params);
/**
* 获取操作描述用于UI显示
*
* @return 操作描述
*/
String getDescription();

View File

@@ -109,7 +109,7 @@ public class PSD_Structure_Dumper {
}
System.out.println(" --- 额外数据块遍历结束 ---");
// 确保指针移动到下一个图层记录的开始
if(fis.getChannel().position() != extraDataEndPos) {
if (fis.getChannel().position() != extraDataEndPos) {
long diff = extraDataEndPos - fis.getChannel().position();
System.out.printf("!!! 指针与预期不符,强制跳过 %d 字节以对齐下一个图层%n", diff);
skipFully(dis, diff);

View File

@@ -1,11 +1,11 @@
package com.chuangzhou.vivid2D.render.model;
public class AnimationParameter {
private String id;
private final String id;
private float value;
private float defaultValue;
private float minValue;
private float maxValue;
private final float defaultValue;
private final float minValue;
private final float maxValue;
private boolean changed = false;
public AnimationParameter(String id, float min, float max, float defaultValue) {
@@ -24,18 +24,33 @@ public class AnimationParameter {
}
}
public boolean hasChanged() { return changed; }
public void markClean() { this.changed = false; }
public boolean hasChanged() {
return changed;
}
public float getValue() { return value; }
public void markClean() {
this.changed = false;
}
public String getId() { return id; }
public float getValue() {
return value;
}
public float getMinValue() { return minValue; }
public String getId() {
return id;
}
public float getMaxValue() { return maxValue; }
public float getMinValue() {
return minValue;
}
public float getDefaultValue() { return defaultValue; }
public float getMaxValue() {
return maxValue;
}
public float getDefaultValue() {
return defaultValue;
}
public void reset() {
this.value = defaultValue;

View File

@@ -1,11 +1,11 @@
package com.chuangzhou.vivid2D.render.model;
import javax.swing.tree.DefaultMutableTreeNode;
import com.chuangzhou.vivid2D.render.model.data.ModelData;
import com.chuangzhou.vivid2D.render.model.data.ModelMetadata;
import com.chuangzhou.vivid2D.render.model.util.*;
import org.joml.Matrix3f;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.*;
/**
@@ -94,6 +94,7 @@ public class Model2D {
}
// ==================== 姿态管理 ====================
/**
* 添加或更新姿态
*/
@@ -559,21 +560,42 @@ public class Model2D {
}
// ==================== Getter/Setter ====================
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getName() {
return name;
}
public UUID getUuid() { return uuid; }
public void setUuid(UUID uuid) { this.uuid = uuid; }
public void setName(String name) {
this.name = name;
}
public ModelMetadata getMetadata() { return metadata; }
public void setMetadata(ModelMetadata metadata) { this.metadata = metadata; }
public UUID getUuid() {
return uuid;
}
public ModelPart getRootPart() { return rootPart; }
public void setRootPart(ModelPart rootPart) { this.rootPart = rootPart; }
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public ModelMetadata getMetadata() {
return metadata;
}
public void setMetadata(ModelMetadata metadata) {
this.metadata = metadata;
}
public ModelPart getRootPart() {
return rootPart;
}
public void setRootPart(ModelPart rootPart) {
this.rootPart = rootPart;
}
public List<Mesh2D> getMeshes() { return Collections.unmodifiableList(meshes); }
public List<Mesh2D> getMeshes() {
return Collections.unmodifiableList(meshes);
}
public Map<String, AnimationParameter> getParameters() {
return Collections.unmodifiableMap(parameters);
@@ -583,17 +605,35 @@ public class Model2D {
return Collections.unmodifiableList(animationLayers);
}
public PhysicsSystem getPhysics() { return physics; }
public PhysicsSystem getPhysics() {
return physics;
}
public ModelPose getCurrentPose() {
return new ModelPose(currentPose);
}
public float getBlendProgress() { return blendProgress; }
public boolean isBlending() { return blendProgress < 1.0f; }
public float getBlendProgress() {
return blendProgress;
}
public boolean isBlending() {
return blendProgress < 1.0f;
}
public Map<String, ModelPose> getPoses() {
return Collections.unmodifiableMap(poses);
}
public BoundingBox getBounds() { return bounds; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public BoundingBox getBounds() {
return bounds;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}

View File

@@ -3,16 +3,16 @@ package com.chuangzhou.vivid2D.render.model;
import com.chuangzhou.vivid2D.render.awt.util.OperationHistoryGlobal;
import com.chuangzhou.vivid2D.render.model.util.BoundingBox;
import com.chuangzhou.vivid2D.render.model.util.Deformer;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.PuppetPin;
import com.chuangzhou.vivid2D.render.systems.Matrix3fUtils;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import org.joml.Matrix3f;
import org.joml.Vector2f;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.*;
/**
* 2D模型部件支持层级变换和变形器
@@ -54,7 +54,7 @@ public class ModelPart {
private final List<ModelEvent> events = new ArrayList<>();
private boolean inMultiSelectionOperation = false;
private boolean startLiquefy =false;
private boolean startLiquefy = false;
// ====== 液化模式枚举 ======
public enum LiquifyMode {
@@ -112,7 +112,7 @@ public class ModelPart {
private void triggerEvent(String eventName) {
for (ModelEvent event : events) {
event.trigger(eventName,this);
event.trigger(eventName, this);
}
}
@@ -527,20 +527,20 @@ public class ModelPart {
if (stroke == null || stroke.points == null) return;
LiquifyMode mode = stroke.mode != null ? stroke.mode : LiquifyMode.PUSH;
for (LiquifyPoint p : stroke.points) {
applyLiquify(new Vector2f(p.x, p.y), stroke.radius, stroke.strength, mode, stroke.iterations,true);
applyLiquify(new Vector2f(p.x, p.y), stroke.radius, stroke.strength, mode, stroke.iterations, true);
}
}
/**
* 对当前部件下所有网格应用液化笔效果(类似 Photoshop 的液化工具)。
* 请在使用前注册在使用addLiquifyStroke方法注册LiquifyStroke
*
* <p>
* 注意:
* - brushCenter 使用世界坐标(与 ModelPart 的世界坐标体系一致)。
* - radius 为画笔半径像素strength 为强度(建议范围 0.0 - 1.0,数值越大效果越强)。
* - mode 选择液化操作类型。
* - iterations 为迭代次数(>0可用来让效果更平滑默认 1 次即可。
*
* <p>
* 该方法会直接修改 mesh 的顶点并更新其边界mesh.updateBounds
*/
public void applyLiquify(Vector2f brushCenter, float radius, float strength, LiquifyMode mode, int iterations, boolean createVertices) {
@@ -928,7 +928,7 @@ public class ModelPart {
float brushArea = brushAreaWidth * brushAreaHeight;
// 根据画笔大小和网格复杂度计算顶点密度
int targetVertexCount = Math.max(4, Math.min(20, (int)(brushArea / (localRadius * localRadius * 0.5f))));
int targetVertexCount = Math.max(4, Math.min(20, (int) (brushArea / (localRadius * localRadius * 0.5f))));
// 在画笔区域内均匀添加顶点
boolean addedVertices = addUniformVerticesInArea(mesh, newVertices, newUVs, newIndices,
@@ -1899,7 +1899,6 @@ public class ModelPart {
}
public void setScale(float uniformScale) {
// 记录旧的世界变换,用于计算 pivot 的相对位置
Matrix3f oldWorldTransform = new Matrix3f(this.worldTransform);
@@ -1992,7 +1991,8 @@ public class ModelPart {
Vector2f origPivot = mesh.getOriginalPivot();
Vector2f worldPivot = Matrix3fUtils.transformPoint(this.worldTransform, origPivot);
mesh.setPivot(worldPivot.x, worldPivot.y);
} catch (Exception ignored) { }
} catch (Exception ignored) {
}
// ==================== 新增:初始化木偶控制点的位置 ====================
initializePuppetPinsPosition(mesh);
@@ -2079,7 +2079,7 @@ public class ModelPart {
// 由于 Mesh2D 的 originalPivot 已经存储了其在 ModelPart 局部坐标系中的相对位置,
// 我们可以直接将 ModelPart 的新 pivot 赋值给 Mesh2D 的 originalPivot
// 然后再通过变换更新 Mesh2D 的实际 pivot
if (!mesh.setOriginalPivot(new Vector2f(x, y))){
if (!mesh.setOriginalPivot(new Vector2f(x, y))) {
return false;
}
// Mesh2D 的实际 pivot 应该根据 ModelPart 的世界变换来计算
@@ -2289,8 +2289,13 @@ public class ModelPart {
return opacity;
}
public float getScaleX() { return scaleX; }
public float getScaleY() { return scaleY; }
public float getScaleX() {
return scaleX;
}
public float getScaleY() {
return scaleY;
}
public void setOpacity(float opacity) {
this.opacity = Math.max(0.0f, Math.min(1.0f, opacity));
@@ -2306,7 +2311,8 @@ public class ModelPart {
public float y;
public float pressure = 1.0f;
public LiquifyPoint() {}
public LiquifyPoint() {
}
public LiquifyPoint(float x, float y) {
this.x = x;
@@ -2319,9 +2325,17 @@ public class ModelPart {
this.pressure = pressure;
}
public float getX() { return x; }
public float getY() { return y; }
public float getPressure() { return pressure; }
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getPressure() {
return pressure;
}
}
// ====== 液化笔划数据结构(包含点序列与笔划参数),提供 getter 以便反射读取 ======
@@ -2332,7 +2346,8 @@ public class ModelPart {
public int iterations = 1;
public List<LiquifyPoint> points = new ArrayList<>();
public LiquifyStroke() {}
public LiquifyStroke() {
}
public LiquifyStroke(LiquifyMode mode, float radius, float strength, int iterations) {
this.mode = mode;
@@ -2341,11 +2356,25 @@ public class ModelPart {
this.iterations = iterations;
}
public String getMode() { return mode.name(); } // PartData 反射时读取字符串也可
public float getRadius() { return radius; }
public float getStrength() { return strength; }
public int getIterations() { return iterations; }
public List<LiquifyPoint> getPoints() { return points; }
public String getMode() {
return mode.name();
} // PartData 反射时读取字符串也可
public float getRadius() {
return radius;
}
public float getStrength() {
return strength;
}
public int getIterations() {
return iterations;
}
public List<LiquifyPoint> getPoints() {
return points;
}
public void addPoint(float x, float y, float pressure) {
this.points.add(new LiquifyPoint(x, y, pressure));

View File

@@ -11,6 +11,7 @@ import java.util.Map;
/**
* 动画层数据
*
* @author tzdwindows 7
*/
public class AnimationLayerData implements Serializable {
@@ -151,7 +152,8 @@ public class AnimationLayerData implements Serializable {
public float value;
public AnimationLayer.InterpolationType interpolation;
public KeyframeData() {}
public KeyframeData() {
}
public KeyframeData(AnimationLayer.Keyframe keyframe) {
this.time = keyframe.getTime();

View File

@@ -286,7 +286,8 @@ public class MeshData implements Serializable {
public Vector2f uv;
public boolean selected;
public SecondaryVertexData() {}
public SecondaryVertexData() {
}
public SecondaryVertexData(SecondaryVertex vertex) {
this.id = vertex.getId();

View File

@@ -20,7 +20,7 @@ import java.util.zip.GZIPOutputStream;
* @author tzdwindows 7
*/
public class ModelData implements Serializable {
private static Logger logger = LoggerFactory.getLogger(ModelData.class);
private static final Logger logger = LoggerFactory.getLogger(ModelData.class);
private static final long serialVersionUID = 1L;
// ==================== 模型元数据 ====================
@@ -39,11 +39,11 @@ public class ModelData implements Serializable {
private List<ParameterData> parameters;
private List<AnimationData> animations;
private List<AnimationLayerData> animationLayers;
private List<ParticleData> physicsParticles;
private List<SpringData> physicsSprings;
private List<ColliderData> physicsColliders;
private List<ConstraintData> physicsConstraints;
private List<LightSourceData> lights;
private final List<ParticleData> physicsParticles;
private final List<SpringData> physicsSprings;
private final List<ColliderData> physicsColliders;
private final List<ConstraintData> physicsConstraints;
private final List<LightSourceData> lights;
private List<PoseData> poses;
private String currentPoseName; // 当前应用的姿态名称
@@ -163,8 +163,7 @@ public class ModelData implements Serializable {
// 约束(仅序列化常见两类)
for (PhysicsSystem.PhysicsConstraint c : phys.getConstraints()) {
if (c instanceof PhysicsSystem.PositionConstraint) {
PhysicsSystem.PositionConstraint pc = (PhysicsSystem.PositionConstraint) c;
if (c instanceof PhysicsSystem.PositionConstraint pc) {
ConstraintData cd = new ConstraintData();
cd.type = "position";
cd.particleId = pc.getParticle().getId();
@@ -174,8 +173,7 @@ public class ModelData implements Serializable {
cd.strength = pc.getStrength();
cd.enabled = pc.isEnabled();
physicsConstraints.add(cd);
} else if (c instanceof PhysicsSystem.DistanceConstraint) {
PhysicsSystem.DistanceConstraint dc = (PhysicsSystem.DistanceConstraint) c;
} else if (c instanceof PhysicsSystem.DistanceConstraint dc) {
ConstraintData cd = new ConstraintData();
cd.type = "distance";
cd.particleId = dc.getParticle().getId();
@@ -193,14 +191,12 @@ public class ModelData implements Serializable {
ColliderData cd = new ColliderData();
cd.id = collider.getId();
cd.enabled = collider.isEnabled();
if (collider instanceof PhysicsSystem.CircleCollider) {
PhysicsSystem.CircleCollider cc = (PhysicsSystem.CircleCollider) collider;
if (collider instanceof PhysicsSystem.CircleCollider cc) {
cd.type = "circle";
cd.centerX = cc.getCenter().x;
cd.centerY = cc.getCenter().y;
cd.radius = cc.getRadius();
} else if (collider instanceof PhysicsSystem.RectangleCollider) {
PhysicsSystem.RectangleCollider rc = (PhysicsSystem.RectangleCollider) collider;
} else if (collider instanceof PhysicsSystem.RectangleCollider rc) {
cd.type = "rect";
cd.centerX = rc.getCenter().x;
cd.centerY = rc.getCenter().y;
@@ -538,7 +534,6 @@ public class ModelData implements Serializable {
}
private Map<String, Texture> deserializeTextures() {
Map<String, Texture> textureMap = new HashMap<>();
@@ -859,7 +854,6 @@ public class ModelData implements Serializable {
// ==================== 内部数据类 ====================
// ---------- 物理数据的序列化类 ----------
public static class ParticleData implements Serializable {
public String id;
@@ -956,7 +950,6 @@ public class ModelData implements Serializable {
}
/**
* 参数数据
*/
@@ -969,7 +962,8 @@ public class ModelData implements Serializable {
public float minValue;
public float maxValue;
public ParameterData() {}
public ParameterData() {
}
public ParameterData(AnimationParameter param) {
this.id = param.getId();
@@ -1042,60 +1036,146 @@ public class ModelData implements Serializable {
// ==================== Getter/Setter ====================
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getName() {
return name;
}
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public void setName(String name) {
this.name = name;
}
public UUID getUuid() { return uuid; }
public void setUuid(UUID uuid) { this.uuid = uuid; }
public String getVersion() {
return version;
}
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public void setVersion(String version) {
this.version = version;
}
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public UUID getUuid() {
return uuid;
}
public long getCreationTime() { return creationTime; }
public void setCreationTime(long creationTime) { this.creationTime = creationTime; }
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public long getLastModifiedTime() { return lastModifiedTime; }
public void setLastModifiedTime(long lastModifiedTime) { this.lastModifiedTime = lastModifiedTime; }
public String getAuthor() {
return author;
}
public List<PartData> getParts() { return parts; }
public void setParts(List<PartData> parts) { this.parts = parts; }
public void setAuthor(String author) {
this.author = author;
}
public List<MeshData> getMeshes() { return meshes; }
public void setMeshes(List<MeshData> meshes) { this.meshes = meshes; }
public String getDescription() {
return description;
}
public List<TextureData> getTextures() { return textures; }
public void setTextures(List<TextureData> textures) { this.textures = textures; }
public void setDescription(String description) {
this.description = description;
}
public List<ParameterData> getParameters() { return parameters; }
public void setParameters(List<ParameterData> parameters) { this.parameters = parameters; }
public long getCreationTime() {
return creationTime;
}
public List<AnimationData> getAnimations() { return animations; }
public void setAnimations(List<AnimationData> animations) { this.animations = animations; }
public void setCreationTime(long creationTime) {
this.creationTime = creationTime;
}
public Vector2f getPivotPoint() { return pivotPoint; }
public void setPivotPoint(Vector2f pivotPoint) { this.pivotPoint = pivotPoint; }
public long getLastModifiedTime() {
return lastModifiedTime;
}
public float getUnitsPerMeter() { return unitsPerMeter; }
public void setUnitsPerMeter(float unitsPerMeter) { this.unitsPerMeter = unitsPerMeter; }
public void setLastModifiedTime(long lastModifiedTime) {
this.lastModifiedTime = lastModifiedTime;
}
public Map<String, String> getUserData() { return userData; }
public void setUserData(Map<String, String> userData) { this.userData = userData; }
public List<PartData> getParts() {
return parts;
}
public void setParts(List<PartData> parts) {
this.parts = parts;
}
public List<MeshData> getMeshes() {
return meshes;
}
public void setMeshes(List<MeshData> meshes) {
this.meshes = meshes;
}
public List<TextureData> getTextures() {
return textures;
}
public void setTextures(List<TextureData> textures) {
this.textures = textures;
}
public List<ParameterData> getParameters() {
return parameters;
}
public void setParameters(List<ParameterData> parameters) {
this.parameters = parameters;
}
public List<AnimationData> getAnimations() {
return animations;
}
public void setAnimations(List<AnimationData> animations) {
this.animations = animations;
}
public Vector2f getPivotPoint() {
return pivotPoint;
}
public void setPivotPoint(Vector2f pivotPoint) {
this.pivotPoint = pivotPoint;
}
public float getUnitsPerMeter() {
return unitsPerMeter;
}
public void setUnitsPerMeter(float unitsPerMeter) {
this.unitsPerMeter = unitsPerMeter;
}
public Map<String, String> getUserData() {
return userData;
}
public void setUserData(Map<String, String> userData) {
this.userData = userData;
}
public List<AnimationLayerData> getAnimationLayers() {
return animationLayers;
}
public List<AnimationLayerData> getAnimationLayers() { return animationLayers; }
public void setAnimationLayers(List<AnimationLayerData> animationLayers) {
this.animationLayers = animationLayers;
}
public List<PoseData> getPoses() { return poses; }
public void setPoses(List<PoseData> poses) { this.poses = poses; }
public List<PoseData> getPoses() {
return poses;
}
public void setPoses(List<PoseData> poses) {
this.poses = poses;
}
public String getCurrentPoseName() {
return currentPoseName;
}
public String getCurrentPoseName() { return currentPoseName; }
public void setCurrentPoseName(String currentPoseName) {
this.currentPoseName = currentPoseName;
}

View File

@@ -270,7 +270,7 @@ public class ModelMetadata implements Serializable, Cloneable {
// 粗略估算:顶点数据 + 纹理数据 + 其他开销
long vertexDataSize = (long) vertexCount * 8 * 2; // 每个顶点8字节float x,y2份原始+变形)
long textureDataSize = (long) textureCount * 1024 * 1024; // 假设每个纹理1MB
long otherDataSize = (long) (parameterCount * 16 + partCount * 64 + polygonCount * 12);
long otherDataSize = parameterCount * 16L + partCount * 64L + polygonCount * 12L;
return vertexDataSize + textureDataSize + otherDataSize + 1024; // +1KB元数据
}

View File

@@ -6,6 +6,7 @@ import java.io.Serializable;
/**
* 部件姿态数据序列化类
*
* @author tzdwindows 7
*/
public class PartPoseData implements Serializable {
@@ -19,7 +20,8 @@ public class PartPoseData implements Serializable {
public boolean visible;
public float colorR, colorG, colorB;
public PartPoseData() {}
public PartPoseData() {
}
public PartPoseData(String partName, ModelPose.PartPose partPose) {
this.partName = partName;

View File

@@ -8,6 +8,7 @@ import java.util.List;
/**
* 姿态数据序列化类
*
* @author tzdwindows 7
*/
public class PoseData implements Serializable {

View File

@@ -4,7 +4,6 @@ import com.chuangzhou.vivid2D.render.model.util.Deformer;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import org.joml.Vector2f;
import java.util.List;
import java.util.Map;
/**
@@ -98,11 +97,23 @@ public class RotationDeformer extends Deformer {
}
// Getter/Setter
public float getBaseAngle() { return baseAngle; }
public void setBaseAngle(float baseAngle) { this.baseAngle = baseAngle; }
public float getBaseAngle() {
return baseAngle;
}
public float getAngleRange() { return angleRange; }
public void setAngleRange(float angleRange) { this.angleRange = angleRange; }
public void setBaseAngle(float baseAngle) {
this.baseAngle = baseAngle;
}
public float getCurrentAngle() { return currentAngle; }
public float getAngleRange() {
return angleRange;
}
public void setAngleRange(float angleRange) {
this.angleRange = angleRange;
}
public float getCurrentAngle() {
return currentAngle;
}
}

View File

@@ -29,9 +29,10 @@ public class VertexDeformer extends Deformer {
}
/**
* 顶点变形数据内部类
*/
private record VertexDeformation(float originalX, float originalY, float targetX, float targetY) { }
* 顶点变形数据内部类
*/
private record VertexDeformation(float originalX, float originalY, float targetX, float targetY) {
}
/**
* 添加顶点变形目标
@@ -212,17 +213,20 @@ public class VertexDeformer extends Deformer {
try {
String enabledKey = map.get(name + ".enabled");
if (enabledKey != null) this.enabled = Boolean.parseBoolean(enabledKey);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
try {
String weightKey = map.get(name + ".weight");
if (weightKey != null) this.weight = Float.parseFloat(weightKey);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
try {
String curKey = map.get(name + ".currentValue");
if (curKey != null) this.currentValue = Float.parseFloat(curKey);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
// 清空已有数据
this.vertexDeformations.clear();

View File

@@ -4,7 +4,6 @@ import com.chuangzhou.vivid2D.render.model.util.Deformer;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import org.joml.Vector2f;
import java.util.List;
import java.util.Map;
/**
@@ -247,7 +246,8 @@ public class WaveDeformer extends Deformer {
public float timeMultiplier = 1.0f;
public float weight = 1.0f;
public WaveConfig() {}
public WaveConfig() {
}
public WaveConfig(float amplitude, float frequency, float phase,
float waveAngle, float timeMultiplier, float weight) {
@@ -287,24 +287,56 @@ public class WaveDeformer extends Deformer {
timeMultiplier = Float.parseFloat(map.get("timeMultiplier"));
}
public float getTimeMultiplier() { return timeMultiplier; }
public void setTimeMultiplier(float timeMultiplier) { this.timeMultiplier = timeMultiplier; }
public float getTimeMultiplier() {
return timeMultiplier;
}
public ControlMode getControlMode() { return controlMode; }
public void setTimeMultiplier(float timeMultiplier) {
this.timeMultiplier = timeMultiplier;
}
public ControlMode getControlMode() {
return controlMode;
}
// Getter/Setter
public float getAmplitude() { return amplitude; }
public void setAmplitude(float amplitude) { this.amplitude = amplitude; }
public float getAmplitude() {
return amplitude;
}
public float getFrequency() { return frequency; }
public void setFrequency(float frequency) { this.frequency = frequency; }
public void setAmplitude(float amplitude) {
this.amplitude = amplitude;
}
public float getPhase() { return phase; }
public void setPhase(float phase) { this.phase = phase; }
public float getFrequency() {
return frequency;
}
public float getWaveAngle() { return waveAngle; }
public void setWaveAngle(float waveAngle) { this.waveAngle = waveAngle; }
public void setFrequency(float frequency) {
this.frequency = frequency;
}
public float getCurrentTime() { return currentTime; }
public void setCurrentTime(float currentTime) { this.currentTime = currentTime; }
public float getPhase() {
return phase;
}
public void setPhase(float phase) {
this.phase = phase;
}
public float getWaveAngle() {
return waveAngle;
}
public void setWaveAngle(float waveAngle) {
this.waveAngle = waveAngle;
}
public float getCurrentTime() {
return currentTime;
}
public void setCurrentTime(float currentTime) {
this.currentTime = currentTime;
}
}

View File

@@ -31,7 +31,7 @@ public class AnimationLayer {
private float currentTime;
private boolean playing;
private boolean paused;
private Map<String, Float> parameterOverrides;
private final Map<String, Float> parameterOverrides;
// ==================== 事件系统 ====================
private final List<AnimationEventListener> eventListeners;
@@ -468,22 +468,45 @@ public class AnimationLayer {
// ==================== Getter/Setter ====================
public String getName() { return name; }
public UUID getUuid() { return uuid; }
public String getName() {
return name;
}
public UUID getUuid() {
return uuid;
}
public float getWeight() {
return weight;
}
public float getWeight() { return weight; }
public void setWeight(float weight) {
this.weight = Math.max(0.0f, Math.min(1.0f, weight));
}
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public boolean isEnabled() {
return enabled;
}
public BlendMode getBlendMode() { return blendMode; }
public void setBlendMode(BlendMode blendMode) { this.blendMode = blendMode; }
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public int getPriority() { return priority; }
public void setPriority(int priority) { this.priority = priority; }
public BlendMode getBlendMode() {
return blendMode;
}
public void setBlendMode(BlendMode blendMode) {
this.blendMode = blendMode;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public Map<String, AnimationTrack> getTracks() {
return Collections.unmodifiableMap(tracks);
@@ -493,20 +516,37 @@ public class AnimationLayer {
return Collections.unmodifiableList(clips);
}
public AnimationClip getCurrentClip() { return currentClip; }
public AnimationClip getCurrentClip() {
return currentClip;
}
public float getPlaybackSpeed() {
return playbackSpeed;
}
public float getPlaybackSpeed() { return playbackSpeed; }
public void setPlaybackSpeed(float playbackSpeed) {
this.playbackSpeed = Math.max(0.0f, playbackSpeed);
}
public boolean isLooping() { return looping; }
public void setLooping(boolean looping) { this.looping = looping; }
public boolean isLooping() {
return looping;
}
public float getCurrentTime() { return currentTime; }
public void setLooping(boolean looping) {
this.looping = looping;
}
public boolean isPlaying() { return playing; }
public boolean isPaused() { return paused; }
public float getCurrentTime() {
return currentTime;
}
public boolean isPlaying() {
return playing;
}
public boolean isPaused() {
return paused;
}
public Map<String, Float> getParameterOverrides() {
return Collections.unmodifiableMap(parameterOverrides);
@@ -616,12 +656,29 @@ public class AnimationLayer {
}
// Getter/Setter
public String getParameterId() { return parameterId; }
public List<Keyframe> getKeyframes() { return Collections.unmodifiableList(keyframes); }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public InterpolationType getInterpolation() { return interpolation; }
public void setInterpolation(InterpolationType interpolation) { this.interpolation = interpolation; }
public String getParameterId() {
return parameterId;
}
public List<Keyframe> getKeyframes() {
return Collections.unmodifiableList(keyframes);
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public InterpolationType getInterpolation() {
return interpolation;
}
public void setInterpolation(InterpolationType interpolation) {
this.interpolation = interpolation;
}
}
/**
@@ -643,9 +700,17 @@ public class AnimationLayer {
}
// Getter
public float getTime() { return time; }
public float getValue() { return value; }
public InterpolationType getInterpolation() { return interpolation; }
public float getTime() {
return time;
}
public float getValue() {
return value;
}
public InterpolationType getInterpolation() {
return interpolation;
}
}
/**
@@ -687,9 +752,17 @@ public class AnimationLayer {
}
// Getter
public String getName() { return name; }
public float getTime() { return time; }
public boolean isTriggered() { return triggered; }
public String getName() {
return name;
}
public float getTime() {
return time;
}
public boolean isTriggered() {
return triggered;
}
}
/**
@@ -697,11 +770,17 @@ public class AnimationLayer {
*/
public interface AnimationEventListener {
void onAnimationStarted(AnimationLayer layer, AnimationClip clip);
void onAnimationStopped(AnimationLayer layer, AnimationClip clip);
void onAnimationPaused(AnimationLayer layer, AnimationClip clip);
void onAnimationResumed(AnimationLayer layer, AnimationClip clip);
void onAnimationCompleted(AnimationLayer layer, AnimationClip clip);
void onAnimationLooped(AnimationLayer layer, AnimationClip clip);
void onEventTriggered(AnimationLayer layer, AnimationEvent event);
}
@@ -709,13 +788,33 @@ public class AnimationLayer {
* 简单的动画事件监听器适配器
*/
public static abstract class AnimationEventAdapter implements AnimationEventListener {
@Override public void onAnimationStarted(AnimationLayer layer, AnimationClip clip) {}
@Override public void onAnimationStopped(AnimationLayer layer, AnimationClip clip) {}
@Override public void onAnimationPaused(AnimationLayer layer, AnimationClip clip) {}
@Override public void onAnimationResumed(AnimationLayer layer, AnimationClip clip) {}
@Override public void onAnimationCompleted(AnimationLayer layer, AnimationClip clip) {}
@Override public void onAnimationLooped(AnimationLayer layer, AnimationClip clip) {}
@Override public void onEventTriggered(AnimationLayer layer, AnimationEvent event) {}
@Override
public void onAnimationStarted(AnimationLayer layer, AnimationClip clip) {
}
@Override
public void onAnimationStopped(AnimationLayer layer, AnimationClip clip) {
}
@Override
public void onAnimationPaused(AnimationLayer layer, AnimationClip clip) {
}
@Override
public void onAnimationResumed(AnimationLayer layer, AnimationClip clip) {
}
@Override
public void onAnimationCompleted(AnimationLayer layer, AnimationClip clip) {
}
@Override
public void onAnimationLooped(AnimationLayer layer, AnimationClip clip) {
}
@Override
public void onEventTriggered(AnimationLayer layer, AnimationEvent event) {
}
}
// ==================== Object 方法 ====================

View File

@@ -229,12 +229,13 @@ public abstract class Deformer {
* 变形范围控制
*/
public static class DeformationRange {
private Vector2f center = new Vector2f(0, 0);
private final Vector2f center = new Vector2f(0, 0);
private float radius = 100.0f;
private float innerRadius = 0.0f;
private float falloff = 2.0f;
public DeformationRange() {}
public DeformationRange() {
}
public DeformationRange(Vector2f center, float radius) {
this.center.set(center);
@@ -263,17 +264,40 @@ public abstract class Deformer {
}
// Getter/Setter
public Vector2f getCenter() { return new Vector2f(center); }
public void setCenter(Vector2f center) { this.center.set(center); }
public void setCenter(float x, float y) { this.center.set(x, y); }
public Vector2f getCenter() {
return new Vector2f(center);
}
public float getRadius() { return radius; }
public void setRadius(float radius) { this.radius = radius; }
public void setCenter(Vector2f center) {
this.center.set(center);
}
public float getInnerRadius() { return innerRadius; }
public void setInnerRadius(float innerRadius) { this.innerRadius = innerRadius; }
public void setCenter(float x, float y) {
this.center.set(x, y);
}
public float getFalloff() { return falloff; }
public void setFalloff(float falloff) { this.falloff = falloff; }
public float getRadius() {
return radius;
}
public void setRadius(float radius) {
this.radius = radius;
}
public float getInnerRadius() {
return innerRadius;
}
public void setInnerRadius(float innerRadius) {
this.innerRadius = innerRadius;
}
public float getFalloff() {
return falloff;
}
public void setFalloff(float falloff) {
this.falloff = falloff;
}
}
}

View File

@@ -7,12 +7,13 @@ import java.awt.*;
/**
* 光源系统
*
* @author tzdwindows 7
*/
public class LightSource {
private Vector2f position;
private Vector3f color;
private float intensity;
private final Vector2f position;
private final Vector3f color;
private final float intensity;
private boolean enabled = true;
private boolean isAmbient = false; // 是否为环境光
@@ -73,31 +74,73 @@ public class LightSource {
return new Color(red, green, blue);
}
public Vector2f getPosition() { return position; }
public Vector3f getColor() { return color; }
public float getIntensity() { return intensity; }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public Vector2f getPosition() {
return position;
}
public Vector3f getColor() {
return color;
}
public float getIntensity() {
return intensity;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
// 判断是否为环境光
public boolean isAmbient() { return isAmbient; }
public void setAmbient(boolean ambient) { this.isAmbient = ambient; }
public boolean isAmbient() {
return isAmbient;
}
public void setAmbient(boolean ambient) {
this.isAmbient = ambient;
}
// ---- 辉光相关的 getter / setter ----
public boolean isGlow() { return isGlow; }
public void setGlow(boolean glow) { this.isGlow = glow; }
public boolean isGlow() {
return isGlow;
}
public void setGlow(boolean glow) {
this.isGlow = glow;
}
public Vector2f getGlowDirection() {
return glowDirection;
}
public Vector2f getGlowDirection() { return glowDirection; }
public void setGlowDirection(Vector2f glowDirection) {
this.glowDirection = glowDirection != null ? glowDirection : new Vector2f(0f, 0f);
}
public float getGlowIntensity() { return glowIntensity; }
public void setGlowIntensity(float glowIntensity) { this.glowIntensity = glowIntensity; }
public float getGlowIntensity() {
return glowIntensity;
}
public float getGlowRadius() { return glowRadius; }
public void setGlowRadius(float glowRadius) { this.glowRadius = glowRadius; }
public void setGlowIntensity(float glowIntensity) {
this.glowIntensity = glowIntensity;
}
public float getGlowAmount() { return glowAmount; }
public void setGlowAmount(float glowAmount) { this.glowAmount = glowAmount; }
public float getGlowRadius() {
return glowRadius;
}
public void setGlowRadius(float glowRadius) {
this.glowRadius = glowRadius;
}
public float getGlowAmount() {
return glowAmount;
}
public void setGlowAmount(float glowAmount) {
this.glowAmount = glowAmount;
}
}

View File

@@ -21,12 +21,12 @@ public class ModelPose {
* 单个部件的姿态数据
*/
public static class PartPose {
private Vector2f position;
private final Vector2f position;
private float rotation;
private Vector2f scale;
private final Vector2f scale;
private float opacity;
private boolean visible;
private Vector3f color; // RGB颜色乘数
private final Vector3f color; // RGB颜色乘数
public PartPose() {
this(new Vector2f(0, 0), 0.0f, new Vector2f(1, 1), 1.0f, true, new Vector3f(1, 1, 1));
@@ -93,23 +93,53 @@ public class ModelPose {
// ================== Getter和Setter ==================
public Vector2f getPosition() { return new Vector2f(position); }
public void setPosition(Vector2f position) { this.position.set(position); }
public Vector2f getPosition() {
return new Vector2f(position);
}
public float getRotation() { return rotation; }
public void setRotation(float rotation) { this.rotation = rotation; }
public void setPosition(Vector2f position) {
this.position.set(position);
}
public Vector2f getScale() { return new Vector2f(scale); }
public void setScale(Vector2f scale) { this.scale.set(scale); }
public float getRotation() {
return rotation;
}
public float getOpacity() { return opacity; }
public void setOpacity(float opacity) { this.opacity = opacity; }
public void setRotation(float rotation) {
this.rotation = rotation;
}
public boolean isVisible() { return visible; }
public void setVisible(boolean visible) { this.visible = visible; }
public Vector2f getScale() {
return new Vector2f(scale);
}
public Vector3f getColor() { return new Vector3f(color); }
public void setColor(Vector3f color) { this.color.set(color); }
public void setScale(Vector2f scale) {
this.scale.set(scale);
}
public float getOpacity() {
return opacity;
}
public void setOpacity(float opacity) {
this.opacity = opacity;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public Vector3f getColor() {
return new Vector3f(color);
}
public void setColor(Vector3f color) {
this.color.set(color);
}
@Override
public boolean equals(Object o) {
@@ -357,14 +387,29 @@ public class ModelPose {
// ================== Getter和Setter ==================
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getName() {
return name;
}
public float getBlendTime() { return blendTime; }
public void setBlendTime(float blendTime) { this.blendTime = Math.max(0, blendTime); }
public void setName(String name) {
this.name = name;
}
public boolean isDefaultPose() { return isDefaultPose; }
public void setDefaultPose(boolean defaultPose) { isDefaultPose = defaultPose; }
public float getBlendTime() {
return blendTime;
}
public void setBlendTime(float blendTime) {
this.blendTime = Math.max(0, blendTime);
}
public boolean isDefaultPose() {
return isDefaultPose;
}
public void setDefaultPose(boolean defaultPose) {
isDefaultPose = defaultPose;
}
// ================== 工具方法 ==================

View File

@@ -12,15 +12,15 @@ public class PuppetPin {
private static int NEXT_ID = 0;
private int id;
private Vector2f position;
private Vector2f originalPosition;
private Vector2f uv;
private final Vector2f position;
private final Vector2f originalPosition;
private final Vector2f uv;
private float influenceRadius = 100.0f;
private boolean selected = false;
private String name;
// 权重贴图(顶点索引 -> 权重值)
private Map<Integer, Float> weightMap = new HashMap<>();
private final Map<Integer, Float> weightMap = new HashMap<>();
public PuppetPin(float x, float y, float u, float v) {
this.id = NEXT_ID++;
@@ -97,18 +97,53 @@ public class PuppetPin {
// ==================== 原有方法 ====================
// Getters and Setters
public Vector2f getPosition() { return new Vector2f(position); }
public void setPosition(float x, float y) { this.position.set(x, y); }
public void setPosition(Vector2f pos) { this.position.set(pos); }
public Vector2f getOriginalPosition() { return new Vector2f(originalPosition); }
public float getInfluenceRadius() { return influenceRadius; }
public void setInfluenceRadius(float radius) { this.influenceRadius = radius; }
public boolean isSelected() { return selected; }
public void setSelected(boolean selected) { this.selected = selected; }
public int getId() { return id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Map<Integer, Float> getWeightMap() { return new HashMap<>(weightMap); }
public Vector2f getPosition() {
return new Vector2f(position);
}
public void setPosition(float x, float y) {
this.position.set(x, y);
}
public void setPosition(Vector2f pos) {
this.position.set(pos);
}
public Vector2f getOriginalPosition() {
return new Vector2f(originalPosition);
}
public float getInfluenceRadius() {
return influenceRadius;
}
public void setInfluenceRadius(float radius) {
this.influenceRadius = radius;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<Integer, Float> getWeightMap() {
return new HashMap<>(weightMap);
}
public void move(float dx, float dy) {
position.add(dx, dy);

View File

@@ -5,8 +5,8 @@ import org.joml.Vector2f;
/**
* 工具类:用于在 Vector2f 和字符串之间进行转换。
* 例如:
* - toString(new Vector2f(1.5f, -2.0f)) => "1.5,-2.0"
* - fromString("1.5,-2.0") => new Vector2f(1.5f, -2.0f)
* - toString(new Vector2f(1.5f, -2.0f)) => "1.5,-2.0"
* - fromString("1.5,-2.0") => new Vector2f(1.5f, -2.0f)
*
* @author tzdwindows 7
*/
@@ -26,9 +26,9 @@ public class SaveVector2f {
/**
* 从字符串解析为 Vector2f。
* 允许的格式:
* - "x,y"
* - "(x,y)"
* - " x , y "
* - "x,y"
* - "(x,y)"
* - " x , y "
* 若格式错误则返回 (0,0)
*/
public static Vector2f fromString(String str) {

View File

@@ -27,11 +27,25 @@ public class SecondaryVertex {
}
// Getter和Setter方法
public Vector2f getPosition() { return new Vector2f(position); }
public Vector2f getOriginalPosition() { return new Vector2f(originalPosition); }
public Vector2f getUV() { return new Vector2f(uv); }
public boolean isSelected() { return selected; }
public int getId() { return id; }
public Vector2f getPosition() {
return new Vector2f(position);
}
public Vector2f getOriginalPosition() {
return new Vector2f(originalPosition);
}
public Vector2f getUV() {
return new Vector2f(uv);
}
public boolean isSelected() {
return selected;
}
public int getId() {
return id;
}
public void setPosition(float x, float y) {
this.position.set(x, y);

View File

@@ -4,6 +4,7 @@ import org.joml.Vector2f;
/**
* 摄像机类
*
* @author tzdwindows 7
*/
public class Camera {
@@ -12,7 +13,8 @@ public class Camera {
private float zPosition = 0.0f;
private boolean enabled = true;
public Camera() {}
public Camera() {
}
public void setPosition(float x, float y) {
position.set(x, y);

View File

@@ -11,20 +11,19 @@ import java.nio.FloatBuffer;
/**
* 简化版 BufferBuilder用于按顶点流构建并一次性绘制几何体。
* 每个顶点格式: float x, float y, float u, float v 共4个 float
*
* <p>
* 用法:
* BufferBuilder bb = new BufferBuilder();
* bb.begin(GL11.GL_LINE_LOOP, 16);
* bb.vertex(x,y,u,v);
* ...
* bb.end(); // 立即绘制并 cleanup
*
* BufferBuilder bb = new BufferBuilder();
* bb.begin(GL11.GL_LINE_LOOP, 16);
* bb.vertex(x,y,u,v);
* ...
* bb.end(); // 立即绘制并 cleanup
* <p>
* 设计原则:简单、可靠、方便把临时多顶点数据提交到 GPU。
*
*
* @author tzdwindows
* @version 1.2
* @since 2025-10-16
* @author tzdwindows
*/
public class BufferBuilder {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BufferBuilder.class);
@@ -33,8 +32,9 @@ public class BufferBuilder {
private int size; // float 数量
private int vertexCount;
private int mode; // GL mode
private RenderState renderState = new RenderState();
private final RenderState renderState = new RenderState();
private boolean stateSaved = false;
// 内置状态,用于构建完成的缓冲区
public static class BuiltBuffer {
private final int vao;
@@ -51,12 +51,25 @@ public class BufferBuilder {
this.renderState = renderState;
}
public RenderState renderState() { return renderState; }
public RenderState renderState() {
return renderState;
}
public int vao() { return vao; }
public int vbo() { return vbo; }
public int vertexCount() { return vertexCount; }
public int mode() { return mode; }
public int vao() {
return vao;
}
public int vbo() {
return vbo;
}
public int vertexCount() {
return vertexCount;
}
public int mode() {
return mode;
}
}
/**

View File

@@ -7,8 +7,8 @@ import org.lwjgl.opengl.GL20;
/**
* 缓冲区上传器
*
* @version 1.1 - 添加颜色支持
* @author tzdwindows 7
* @version 1.1 - 添加颜色支持
*/
public class BufferUploader {
@@ -65,7 +65,8 @@ public class BufferUploader {
}
if (currentProgram != 0) {
int colorLoc = RenderSystem.getUniformLocation(currentProgram, "uColor");
if (colorLoc == -1) {} else {
if (colorLoc == -1) {
} else {
RenderSystem.uniform4f(colorLoc,
state.color.x, state.color.y, state.color.z, state.color.w);
}

View File

@@ -5,8 +5,8 @@ import com.chuangzhou.vivid2D.render.systems.RenderSystem;
/**
* 管理缓存
*
* @version 1.0
* @author tzdwindows
* @version 1.0
*/
public class Tesselator {
private static final int DEFAULT_BUFFER_SIZE = 2097152; // 2MB

View File

@@ -3,12 +3,18 @@ package com.chuangzhou.vivid2D.render.systems.sources;
/**
* 完整着色器接口
* 一个完整的着色器程序需要顶点着色器和片段着色器
*
* @author tzdwindows 7
*/
public interface CompleteShader {
Shader getVertexShader();
Shader getFragmentShader();
String getShaderName();
boolean isDefaultShader();
default void setDefaultUniforms(ShaderProgram program) {}
default void setDefaultUniforms(ShaderProgram program) {
}
}

View File

@@ -2,9 +2,11 @@ package com.chuangzhou.vivid2D.render.systems.sources;
/**
* 着色器接口
*
* @author tzdwindows 7
*/
public interface Shader {
String getShaderCode();
String getShaderName();
}

View File

@@ -8,104 +8,105 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
public class FragmentShaders implements Shader {
public static final String FRAGMENT_SHADER_SRC =
"""
#version 330 core
in vec2 vTexCoord;
in vec2 vWorldPos;
out vec4 FragColor;
uniform sampler2D uTexture;
uniform vec4 uColor;
uniform float uOpacity;
uniform int uBlendMode;
uniform int uDebugMode;
#define MAX_LIGHTS 8
uniform vec2 uLightsPos[MAX_LIGHTS];
uniform vec3 uLightsColor[MAX_LIGHTS];
uniform float uLightsIntensity[MAX_LIGHTS];
uniform int uLightsIsAmbient[MAX_LIGHTS];
uniform int uLightCount;
// 常用衰减系数(可在 shader 内微调)
const float ATT_CONST = 1.0;
const float ATT_LINEAR = 0.09;
const float ATT_QUAD = 0.032;
void main() {
// 先采样纹理
vec4 tex = texture(uTexture, vTexCoord);
float alpha = tex.a * uOpacity;
if (alpha <= 0.001) discard;
// 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色)
if (uLightCount == 0) {
vec3 base = tex.rgb * uColor.rgb;
// 简单的色调映射(防止数值过大)
base = clamp(base, 0.0, 1.0);
FragColor = vec4(base, alpha);
return;
}
// 基础颜色(纹理 * 部件颜色)
vec3 baseColor = tex.rgb * uColor.rgb;
// 全局环境光基线(可以适度提高以避免全黑)
vec3 ambient = vec3(0.06); // 小环境光补偿
vec3 lighting = vec3(0.0);
vec3 specularAccum = vec3(0.0);
// 累积环境光(来自被标记为环境光的光源)
for (int i = 0; i < uLightCount; ++i) {
if (uLightsIsAmbient[i] == 1) {
lighting += uLightsColor[i] * uLightsIntensity[i];
#version 330 core
in vec2 vTexCoord;
in vec2 vWorldPos;
out vec4 FragColor;
uniform sampler2D uTexture;
uniform vec4 uColor;
uniform float uOpacity;
uniform int uBlendMode;
uniform int uDebugMode;
#define MAX_LIGHTS 8
uniform vec2 uLightsPos[MAX_LIGHTS];
uniform vec3 uLightsColor[MAX_LIGHTS];
uniform float uLightsIntensity[MAX_LIGHTS];
uniform int uLightsIsAmbient[MAX_LIGHTS];
uniform int uLightCount;
// 常用衰减系数(可在 shader 内微调)
const float ATT_CONST = 1.0;
const float ATT_LINEAR = 0.09;
const float ATT_QUAD = 0.032;
void main() {
// 先采样纹理
vec4 tex = texture(uTexture, vTexCoord);
float alpha = tex.a * uOpacity;
if (alpha <= 0.001) discard;
// 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色)
if (uLightCount == 0) {
vec3 base = tex.rgb * uColor.rgb;
// 简单的色调映射(防止数值过大)
base = clamp(base, 0.0, 1.0);
FragColor = vec4(base, alpha);
return;
}
// 基础颜色(纹理 * 部件颜色)
vec3 baseColor = tex.rgb * uColor.rgb;
// 全局环境光基线(可以适度提高以避免全黑)
vec3 ambient = vec3(0.06); // 小环境光补偿
vec3 lighting = vec3(0.0);
vec3 specularAccum = vec3(0.0);
// 累积环境光(来自被标记为环境光的光源)
for (int i = 0; i < uLightCount; ++i) {
if (uLightsIsAmbient[i] == 1) {
lighting += uLightsColor[i] * uLightsIntensity[i];
}
}
// 加上基线环境光
lighting += ambient;
// 对每个非环境光计算基于距离的衰减与简单高光
for (int i = 0; i < uLightCount; ++i) {
if (uLightsIsAmbient[i] == 1) continue;
vec2 toLight = uLightsPos[i] - vWorldPos;
float dist = length(toLight);
// 标准物理式衰减
float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist);
// 强度受光源强度和衰减影响
float radiance = uLightsIntensity[i] * attenuation;
// 漫反射在纯2D情景下法线与视线近似固定Z向
// 所以漫反射对所有片元是恒定的。我们用一个基于距离的柔和因子来模拟明暗变化。
float diffuseFactor = clamp(1.0 - (dist * 0.0015), 0.0, 1.0); // 通过调节常数控制半径感觉
vec3 diff = uLightsColor[i] * radiance * diffuseFactor;
lighting += diff;
// 简单高光(基于视向与反射的大致模拟,产生亮点)
vec3 lightDir3 = normalize(vec3(toLight, 0.0));
vec3 viewDir = vec3(0.0, 0.0, 1.0);
vec3 normal = vec3(0.0, 0.0, 1.0);
vec3 reflectDir = reflect(-lightDir3, normal);
float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); // 16 为高光粗糙度,可调
float specIntensity = 0.2; // 高光强度系数
specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity;
}
// 限制光照的最大值以避免过曝(可根据场景调整)
vec3 totalLighting = min(lighting + specularAccum, vec3(2.0));
// 将光照应用到基础颜色
vec3 finalColor = baseColor * totalLighting;
// 支持简单混合模式(保留原有行为)
if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb;
else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb;
else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb);
finalColor = clamp(finalColor, 0.0, 1.0);
FragColor = vec4(finalColor, alpha);
}
}
// 加上基线环境光
lighting += ambient;
// 对每个非环境光计算基于距离的衰减与简单高光
for (int i = 0; i < uLightCount; ++i) {
if (uLightsIsAmbient[i] == 1) continue;
vec2 toLight = uLightsPos[i] - vWorldPos;
float dist = length(toLight);
// 标准物理式衰减
float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist);
// 强度受光源强度和衰减影响
float radiance = uLightsIntensity[i] * attenuation;
// 漫反射在纯2D情景下法线与视线近似固定Z向
// 所以漫反射对所有片元是恒定的。我们用一个基于距离的柔和因子来模拟明暗变化。
float diffuseFactor = clamp(1.0 - (dist * 0.0015), 0.0, 1.0); // 通过调节常数控制半径感觉
vec3 diff = uLightsColor[i] * radiance * diffuseFactor;
lighting += diff;
// 简单高光(基于视向与反射的大致模拟,产生亮点)
vec3 lightDir3 = normalize(vec3(toLight, 0.0));
vec3 viewDir = vec3(0.0, 0.0, 1.0);
vec3 normal = vec3(0.0, 0.0, 1.0);
vec3 reflectDir = reflect(-lightDir3, normal);
float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); // 16 为高光粗糙度,可调
float specIntensity = 0.2; // 高光强度系数
specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity;
}
// 限制光照的最大值以避免过曝(可根据场景调整)
vec3 totalLighting = min(lighting + specularAccum, vec3(2.0));
// 将光照应用到基础颜色
vec3 finalColor = baseColor * totalLighting;
// 支持简单混合模式(保留原有行为)
if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb;
else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb;
else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb);
finalColor = clamp(finalColor, 0.0, 1.0);
FragColor = vec4(finalColor, alpha);
}
""";
""";
@Override
public String getShaderCode() {
return FRAGMENT_SHADER_SRC;

View File

@@ -5,28 +5,29 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
/**
* 纯色着色器的片段着色器
* 只使用颜色,忽略纹理
*
* @author tzdwindows 7
*/
public class SolidColorFragmentShader implements Shader {
public static final String FRAGMENT_SHADER_SRC =
"""
#version 330 core
out vec4 FragColor;
uniform vec4 uColor;
uniform float uOpacity;
void main() {
// 直接使用颜色,忽略纹理
vec4 finalColor = uColor;
finalColor.a *= uOpacity;
// 如果透明度太低则丢弃片段
if (finalColor.a <= 0.001) discard;
FragColor = finalColor;
}
""";
#version 330 core
out vec4 FragColor;
uniform vec4 uColor;
uniform float uOpacity;
void main() {
// 直接使用颜色,忽略纹理
vec4 finalColor = uColor;
finalColor.a *= uOpacity;
// 如果透明度太低则丢弃片段
if (finalColor.a <= 0.001) discard;
FragColor = finalColor;
}
""";
@Override
public String getShaderCode() {

View File

@@ -7,6 +7,7 @@ import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
/**
* 纯色着色器程序
* 专门用于绘制纯色几何体,如选中框、调试图形等
*
* @author tzdwindows 7
*/
public class SolidColorShader implements CompleteShader {

View File

@@ -4,25 +4,26 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
/**
* 纯色着色器的顶点着色器
*
* @author tzdwindows 7
*/
public class SolidColorVertexShader implements Shader {
public static final String VERTEX_SHADER_SRC =
"""
#version 330 core
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
uniform mat3 uModelMatrix;
uniform mat3 uViewMatrix;
uniform mat3 uProjectionMatrix;
void main() {
// 使用 3x3 矩阵链计算屏幕位置
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
gl_Position = vec4(p.xy, 0.0, 1.0);
}
""";
#version 330 core
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
uniform mat3 uModelMatrix;
uniform mat3 uViewMatrix;
uniform mat3 uProjectionMatrix;
void main() {
// 使用 3x3 矩阵链计算屏幕位置
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
gl_Position = vec4(p.xy, 0.0, 1.0);
}
""";
@Override
public String getShaderCode() {

View File

@@ -12,25 +12,25 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
public class VertexShaders implements Shader {
public static final String VERTEX_SHADER_SRC =
"""
#version 330 core
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
out vec2 vTexCoord;
out vec2 vWorldPos;
uniform mat3 uModelMatrix;
uniform mat3 uViewMatrix;
uniform mat3 uProjectionMatrix;
void main() {
// 使用 3x3 矩阵链计算屏幕位置(假设矩阵是二维仿射)
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
gl_Position = vec4(p.xy, 0.0, 1.0);
vTexCoord = aTexCoord;
// 输出 world-space 位置供 fragment shader 使用(仅 xy
vWorldPos = (uModelMatrix * vec3(aPosition, 1.0)).xy;
}
""";
#version 330 core
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec2 aTexCoord;
out vec2 vTexCoord;
out vec2 vWorldPos;
uniform mat3 uModelMatrix;
uniform mat3 uViewMatrix;
uniform mat3 uProjectionMatrix;
void main() {
// 使用 3x3 矩阵链计算屏幕位置(假设矩阵是二维仿射)
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
gl_Position = vec4(p.xy, 0.0, 1.0);
vTexCoord = aTexCoord;
// 输出 world-space 位置供 fragment shader 使用(仅 xy
vWorldPos = (uModelMatrix * vec3(aPosition, 1.0)).xy;
}
""";
@Override
public String getShaderCode() {

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 {