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

View File

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

View File

@@ -11,9 +11,10 @@ import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
public interface ModelClickListener { public interface ModelClickListener {
/** /**
* 当点击模型时触发 * 当点击模型时触发
* @param mesh 被点击的网格,如果点击在空白处则为 null *
* @param modelX 模型坐标系中的 X 坐标 * @param mesh 被点击的网格,如果点击在空白处则为 null
* @param modelY 模型坐标系中的 Y 坐标 * @param modelX 模型坐标系中的 X 坐标
* @param modelY 模型坐标系中的 Y 坐标
* @param screenX 屏幕坐标系中的 X 坐标 * @param screenX 屏幕坐标系中的 X 坐标
* @param screenY 屏幕坐标系中的 Y 坐标 * @param screenY 屏幕坐标系中的 Y 坐标
*/ */
@@ -21,22 +22,32 @@ public interface ModelClickListener {
/** /**
* 当鼠标在模型上移动时触发 * 当鼠标在模型上移动时触发
* @param mesh 鼠标下方的网格,如果不在任何网格上则为 null *
* @param modelX 模型坐标系中的 X 坐标 * @param mesh 鼠标下方的网格,如果不在任何网格上则为 null
* @param modelY 模型坐标系中的 Y 坐标 * @param modelX 模型坐标系中的 X 坐标
* @param modelY 模型坐标系中的 Y 坐标
* @param screenX 屏幕坐标系中的 X 坐标 * @param screenX 屏幕坐标系中的 X 坐标
* @param screenY 屏幕坐标系中的 Y 坐标 * @param screenY 屏幕坐标系中的 Y 坐标
*/ */
default void onModelHover(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {} default void onModelHover(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {
default void onLiquifyModeExited(){}; }
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.*;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable; 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.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException; 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.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
@@ -30,13 +34,13 @@ import java.util.concurrent.Callable;
/** /**
* ModelLayerPanel完整实现 * ModelLayerPanel完整实现
* * <p>
* - 列表显示“从上到下”的图层listModel[0] 为最上层) * - 列表显示“从上到下”的图层listModel[0] 为最上层)
* - 在任何修改后都会把 model.parts 同步为列表的反序(保证渲染顺序与 UI 一致) * - 在任何修改后都会把 model.parts 同步为列表的反序(保证渲染顺序与 UI 一致)
* - 支持添加空层 / 从文件创建带贴图的层(在有 renderPanel 时在 GL 线程使用 Texture.createFromFile * - 支持添加空层 / 从文件创建带贴图的层(在有 renderPanel 时在 GL 线程使用 Texture.createFromFile
* - 支持为选中部件绑定贴图、创建透明图层 * - 支持为选中部件绑定贴图、创建透明图层
* - 支持拖拽重排、上下按钮移动,并在重排后正确恢复选中与不触发滑块事件 * - 支持拖拽重排、上下按钮移动,并在重排后正确恢复选中与不触发滑块事件
* * <p>
* 使用: * 使用:
* new ModelLayerPanel(model, optionalModelRenderPanel) * new ModelLayerPanel(model, optionalModelRenderPanel)
*/ */
@@ -242,7 +246,8 @@ public class ModelLayerPanel extends JPanel {
if (partMap != null) { if (partMap != null) {
partMap.put(uniqueName, part); partMap.put(uniqueName, part);
} }
} catch (Exception ignored) {} } catch (Exception ignored) {
}
part.setVisible(layerInfo.visible); part.setVisible(layerInfo.visible);
@@ -293,7 +298,8 @@ public class ModelLayerPanel extends JPanel {
// 强制尝试上传/初始化(若纹理对象需要) // 强制尝试上传/初始化(若纹理对象需要)
try { try {
tryCallTextureUpload(texture); tryCallTextureUpload(texture);
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
// 标记模型需要更新 // 标记模型需要更新
model.markNeedsUpdate(); model.markNeedsUpdate();
@@ -306,10 +312,12 @@ public class ModelLayerPanel extends JPanel {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
try { try {
reloadFromModel(); reloadFromModel();
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
try { try {
if (renderPanel != null) renderPanel.repaint(); if (renderPanel != null) renderPanel.repaint();
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
}); });
} else { } else {
System.err.println("创建纹理失败: " + uniqueName); System.err.println("创建纹理失败: " + uniqueName);
@@ -505,7 +513,8 @@ public class ModelLayerPanel extends JPanel {
f.setAccessible(true); f.setAccessible(true);
Object v = f.get(sel); Object v = f.get(sel);
if (v instanceof Float) op = (Float) v; if (v instanceof Float) op = (Float) v;
} catch (Exception ignored) {} } catch (Exception ignored) {
}
} }
int val = Math.round(op * 100); int val = Math.round(op * 100);
@@ -604,7 +613,8 @@ public class ModelLayerPanel extends JPanel {
Field f = sel.getClass().getDeclaredField("opacity"); Field f = sel.getClass().getDeclaredField("opacity");
f.setAccessible(true); f.setAccessible(true);
f.setFloat(sel, val / 100.0f); f.setFloat(sel, val / 100.0f);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
} }
if (model != null) model.markNeedsUpdate(); if (model != null) model.markNeedsUpdate();
layerList.repaint(); layerList.repaint();
@@ -711,7 +721,8 @@ public class ModelLayerPanel extends JPanel {
w = Math.max(1, Integer.parseInt(sp[0].trim())); w = Math.max(1, Integer.parseInt(sp[0].trim()));
h = Math.max(1, Integer.parseInt(sp[1].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); BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
ModelPart part = model.createPart(name); ModelPart part = model.createPart(name);
@@ -753,20 +764,23 @@ public class ModelLayerPanel extends JPanel {
File f = chooser.getSelectedFile(); File f = chooser.getSelectedFile();
try { try {
BufferedImage img = null; BufferedImage img = null;
try { img = ImageIO.read(f); } catch (Exception ignored) {} try {
img = ImageIO.read(f);
} catch (Exception ignored) {
}
// 获取第一个 mesh // 获取第一个 mesh
Mesh2D targetMesh = null; Mesh2D targetMesh = null;
try { try {
Method getMeshes = sel.getClass().getMethod("getMeshes"); Method getMeshes = sel.getClass().getMethod("getMeshes");
Object list = getMeshes.invoke(sel); Object list = getMeshes.invoke(sel);
if (list instanceof List) { if (list instanceof List<?> meshes) {
List<?> meshes = (List<?>) list;
if (!meshes.isEmpty() && meshes.get(0) instanceof Mesh2D) { if (!meshes.isEmpty() && meshes.get(0) instanceof Mesh2D) {
targetMesh = (Mesh2D) meshes.get(0); targetMesh = (Mesh2D) meshes.get(0);
} }
} }
} catch (Exception ignored) {} } catch (Exception ignored) {
}
if (targetMesh == null) { if (targetMesh == null) {
if (img == null) { if (img == null) {
@@ -828,12 +842,12 @@ public class ModelLayerPanel extends JPanel {
try { try {
Method m = Mesh2D.class.getMethod("createQuad", String.class, float.class, float.class); Method m = Mesh2D.class.getMethod("createQuad", String.class, float.class, float.class);
Object o = m.invoke(null, meshName, w, h); Object o = m.invoke(null, meshName, w, h);
if (o instanceof Mesh2D) { if (o instanceof Mesh2D mesh) {
Mesh2D mesh = (Mesh2D) o;
// 对基础四边形进行细分以增加顶点密度 // 对基础四边形进行细分以增加顶点密度
return subdivideMeshForLiquify(mesh, 3); // 3级细分 return subdivideMeshForLiquify(mesh, 3); // 3级细分
} }
} catch (Exception ignored) {} } catch (Exception ignored) {
}
try { try {
// 创建高密度网格(细分网格) // 创建高密度网格(细分网格)
@@ -914,8 +928,7 @@ public class ModelLayerPanel extends JPanel {
if (cons != null) { if (cons != null) {
cons.setAccessible(true); cons.setAccessible(true);
Object meshObj = cons.newInstance(name, vertices, uvs, indices); Object meshObj = cons.newInstance(name, vertices, uvs, indices);
if (meshObj instanceof Mesh2D) { if (meshObj instanceof Mesh2D mesh) {
Mesh2D mesh = (Mesh2D) meshObj;
// 设置合适的pivot中心点 // 设置合适的pivot中心点
mesh.setPivot(0, 0); mesh.setPivot(0, 0);
@@ -1074,11 +1087,12 @@ public class ModelLayerPanel extends JPanel {
if (renderPanel == null) throw new IllegalStateException("需要 renderPanel 才能在 GL 上下文创建纹理"); if (renderPanel == null) throw new IllegalStateException("需要 renderPanel 才能在 GL 上下文创建纹理");
try { try {
return renderPanel.executeInGLContext((Callable<Texture>) () -> { return renderPanel.executeInGLContext(() -> {
// 静态工厂尝试 // 静态工厂尝试
try { try {
Method factory = findStaticMethod(Texture.class, "createFromBufferedImage", BufferedImage.class); 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) { if (factory != null) {
Object texObj = factory.invoke(null, img); Object texObj = factory.invoke(null, img);
if (texObj instanceof Texture) { if (texObj instanceof Texture) {
@@ -1086,7 +1100,8 @@ public class ModelLayerPanel extends JPanel {
return (Texture) texObj; return (Texture) texObj;
} }
} }
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
// 构造 ByteBuffer 并尝试构造器 // 构造 ByteBuffer 并尝试构造器
try { try {
@@ -1123,15 +1138,20 @@ public class ModelLayerPanel extends JPanel {
} }
} }
} }
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
if (formatEnum != null) { if (formatEnum != null) {
try { try {
texObj = suit.newInstance(texName, w, h, formatEnum, buf); texObj = suit.newInstance(texName, w, h, formatEnum, buf);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
} }
} }
if (texObj == null) { 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) { if (texObj instanceof Texture) {
tryCallTextureUpload((Texture) texObj); tryCallTextureUpload((Texture) texObj);
@@ -1185,13 +1205,20 @@ public class ModelLayerPanel extends JPanel {
} }
} }
} }
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
if (formatEnum != null) { 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) { 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; if (texObj instanceof Texture) return (Texture) texObj;
} }
@@ -1245,12 +1272,14 @@ public class ModelLayerPanel extends JPanel {
try { try {
Method m = cls.getMethod(name, param); Method m = cls.getMethod(name, param);
if (Modifier.isStatic(m.getModifiers())) return m; if (Modifier.isStatic(m.getModifiers())) return m;
} catch (Exception ignored) {} } catch (Exception ignored) {
}
try { try {
Method m = cls.getDeclaredMethod(name, param); Method m = cls.getDeclaredMethod(name, param);
m.setAccessible(true); m.setAccessible(true);
if (Modifier.isStatic(m.getModifiers())) return m; if (Modifier.isStatic(m.getModifiers())) return m;
} catch (Exception ignored) {} } catch (Exception ignored) {
}
return null; return null;
} }
@@ -1277,7 +1306,8 @@ public class ModelLayerPanel extends JPanel {
model.setRootPart(null); model.setRootPart(null);
} }
} }
} catch (Exception ignored) {} } catch (Exception ignored) {
}
model.markNeedsUpdate(); model.markNeedsUpdate();
reloadFromModel(); reloadFromModel();
} catch (Exception ex) { } catch (Exception ex) {
@@ -1449,9 +1479,7 @@ public class ModelLayerPanel extends JPanel {
Field partsField = model.getClass().getDeclaredField("parts"); Field partsField = model.getClass().getDeclaredField("parts");
partsField.setAccessible(true); partsField.setAccessible(true);
Object old = partsField.get(model); Object old = partsField.get(model);
if (old instanceof java.util.List) { if (old instanceof @SuppressWarnings("rawtypes")List rawList) {
@SuppressWarnings("rawtypes")
java.util.List rawList = (java.util.List) old;
rawList.clear(); rawList.clear();
rawList.addAll(newParts); rawList.addAll(newParts);
} else { } else {
@@ -1465,9 +1493,9 @@ public class ModelLayerPanel extends JPanel {
// ============== 列表渲染/拖拽辅助 ============== // ============== 列表渲染/拖拽辅助 ==============
private class LayerCellRenderer extends JPanel implements ListCellRenderer<ModelPart> { private class LayerCellRenderer extends JPanel implements ListCellRenderer<ModelPart> {
private JCheckBox visibleBox = new JCheckBox(); private final JCheckBox visibleBox = new JCheckBox();
private JLabel nameLabel = new JLabel(); private final JLabel nameLabel = new JLabel();
private JLabel opacityLabel = new JLabel(); private final JLabel opacityLabel = new JLabel();
LayerCellRenderer() { LayerCellRenderer() {
setLayout(new BorderLayout(6, 6)); setLayout(new BorderLayout(6, 6));
@@ -1543,7 +1571,9 @@ public class ModelLayerPanel extends JPanel {
} }
@Override @Override
public int getSourceActions(JComponent c) { return MOVE; } public int getSourceActions(JComponent c) {
return MOVE;
}
@Override @Override
public boolean canImport(TransferSupport support) { public boolean canImport(TransferSupport support) {
@@ -1638,23 +1668,25 @@ public class ModelLayerPanel extends JPanel {
try { try {
Field fx = p.getClass().getDeclaredField("pivotX"); Field fx = p.getClass().getDeclaredField("pivotX");
Field fy = p.getClass().getDeclaredField("pivotY"); Field fy = p.getClass().getDeclaredField("pivotY");
fx.setAccessible(true); fy.setAccessible(true); fx.setAccessible(true);
fy.setAccessible(true);
px = ((Number) fx.get(p)).floatValue(); px = ((Number) fx.get(p)).floatValue();
py = ((Number) fy.get(p)).floatValue(); py = ((Number) fy.get(p)).floatValue();
} catch (Exception ignored2) {} } catch (Exception ignored2) {
}
} }
try { try {
Method vm = p.getClass().getMethod("isVisible"); Method vm = p.getClass().getMethod("isVisible");
visible = (Boolean) vm.invoke(p); visible = (Boolean) vm.invoke(p);
} catch (Exception ignored) {} } catch (Exception ignored) {
System.out.println(String.format("Part[%d] name=%s visible=%s pivot=(%.1f, %.1f)", i, name, visible, px, py)); }
System.out.printf("Part[%d] name=%s visible=%s pivot=(%.1f, %.1f)%n", i, name, visible, px, py);
// meshes // meshes
try { try {
Method gmsh = p.getClass().getMethod("getMeshes"); Method gmsh = p.getClass().getMethod("getMeshes");
Object list = gmsh.invoke(p); Object list = gmsh.invoke(p);
if (list instanceof List) { if (list instanceof List<?> meshes) {
List<?> meshes = (List<?>) list;
System.out.println(" meshes count = " + meshes.size()); System.out.println(" meshes count = " + meshes.size());
for (int m = 0; m < meshes.size(); m++) { for (int m = 0; m < meshes.size(); m++) {
Object mesh = meshes.get(m); Object mesh = meshes.get(m);
@@ -1667,7 +1699,8 @@ public class ModelLayerPanel extends JPanel {
Field f = mesh.getClass().getDeclaredField("texture"); Field f = mesh.getClass().getDeclaredField("texture");
f.setAccessible(true); f.setAccessible(true);
tex = f.get(mesh); tex = f.get(mesh);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
} }
System.out.println(" mesh[" + m + "] texture = " + (tex == null ? "null" : tex.getClass().getSimpleName())); 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 com.chuangzhou.vivid2D.test.TestModelGLPanel;
import org.joml.Matrix3f; import org.joml.Matrix3f;
import org.joml.Vector2f; import org.joml.Vector2f;
import org.lwjgl.glfw.*; import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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.Timer;
import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.lang.reflect.Method;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.*; import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.List; import java.util.List;
import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
/** /**
* vivid2D 模型的 Java 渲染面板 * vivid2D 模型的 Java 渲染面板
@@ -43,11 +42,10 @@ import java.util.concurrent.atomic.AtomicReference;
* <p>该类提供了 vivid2D 模型在 Java 环境下的图形渲染功能, * <p>该类提供了 vivid2D 模型在 Java 环境下的图形渲染功能,
* 包含基本的 2D 图形绘制、模型显示和交互操作。</p> * 包含基本的 2D 图形绘制、模型显示和交互操作。</p>
* *
*
* @author tzdwindows 7 * @author tzdwindows 7
* @version 1.1 * @version 1.1
* @since 2025-10-13
* @see TestModelGLPanel * @see TestModelGLPanel
* @since 2025-10-13
*/ */
public class ModelRenderPanel extends JPanel { public class ModelRenderPanel extends JPanel {
private static final Logger logger = LoggerFactory.getLogger(ModelRenderPanel.class); 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 dragStartX, dragStartY;
private volatile float partStartX, partStartY; private volatile float partStartX, partStartY;
private volatile boolean isDragging = false; private volatile boolean isDragging = false;
private enum DragMode { private enum DragMode {
NONE, // 无拖拽 NONE, // 无拖拽
MOVE, // 移动部件 MOVE, // 移动部件
@@ -110,8 +109,8 @@ public class ModelRenderPanel extends JPanel {
public static final float BORDER_THICKNESS = 6.0f; public static final float BORDER_THICKNESS = 6.0f;
public static final float CORNER_SIZE = 12.0f; public static final float CORNER_SIZE = 12.0f;
private volatile float partInitialScaleX = 1.0f; private final float partInitialScaleX = 1.0f;
private volatile float partInitialScaleY = 1.0f; private final float partInitialScaleY = 1.0f;
private volatile float displayScale = 1.0f; // 当前可视缩放(用于检测阈值/角点等) private volatile float displayScale = 1.0f; // 当前可视缩放(用于检测阈值/角点等)
private volatile float targetScale = 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 static final float ZOOM_MAX = 8.0f;
private volatile boolean shiftDuringDrag = false; private volatile boolean shiftDuringDrag = false;
private volatile float rotationStartAngle = 0.0f; private volatile float rotationStartAngle = 0.0f;
private volatile float partInitialRotation = 0.0f; private final float partInitialRotation = 0.0f;
private volatile Vector2f rotationCenter = new Vector2f(); private final Vector2f rotationCenter = new Vector2f();
private static final float ROTATION_HANDLE_DISTANCE = 30.0f; private static final float ROTATION_HANDLE_DISTANCE = 30.0f;
private OperationHistoryManager historyManager; private OperationHistoryManager historyManager;
// 新增:操作历史管理器 // 新增:操作历史管理器
private OperationHistoryGlobal operationHistory; private final OperationHistoryGlobal operationHistory;
// 新增:拖拽操作的状态记录字段 // 新增:拖拽操作的状态记录字段
private Map<ModelPart, Vector2f> dragStartPositions = new HashMap<>(); private final Map<ModelPart, Vector2f> dragStartPositions = new HashMap<>();
private Map<ModelPart, Vector2f> dragStartScales = new HashMap<>(); private final Map<ModelPart, Vector2f> dragStartScales = new HashMap<>();
private Map<ModelPart, Float> dragStartRotations = new HashMap<>(); private final Map<ModelPart, Float> dragStartRotations = new HashMap<>();
private Map<ModelPart, Vector2f> dragStartPivots = new HashMap<>(); private final Map<ModelPart, Vector2f> dragStartPivots = new HashMap<>();
// 新增:鼠标手势相关字段 // 新增:鼠标手势相关字段
private volatile Cursor currentCursor = Cursor.getDefaultCursor(); private volatile Cursor currentCursor = Cursor.getDefaultCursor();
private volatile DragMode hoverDragMode = DragMode.NONE; private final DragMode hoverDragMode = DragMode.NONE;
private volatile boolean isOverSelection = false; private volatile boolean isOverSelection = false;
// ================== 摄像机控制相关字段 ================== // ================== 摄像机控制相关字段 ==================
@@ -1036,7 +1035,7 @@ public class ModelRenderPanel extends JPanel {
* 退出液化模式 * 退出液化模式
*/ */
private void exitLiquifyMode() { private void exitLiquifyMode() {
executeInGLContext(()->{ executeInGLContext(() -> {
liquifyMode = false; liquifyMode = false;
if (liquifyTargetPart != null) { if (liquifyTargetPart != null) {
liquifyTargetPart.setStartLiquefy(false); liquifyTargetPart.setStartLiquefy(false);
@@ -1112,7 +1111,7 @@ public class ModelRenderPanel extends JPanel {
// 绘制圆圈 // 绘制圆圈
int center = size / 2; int center = size / 2;
int radius = (int)(liquifyBrushSize * 0.1f); // 根据画笔大小缩放光标 int radius = (int) (liquifyBrushSize * 0.1f); // 根据画笔大小缩放光标
// 外圈 // 外圈
g2d.setColor(Color.RED); g2d.setColor(Color.RED);
@@ -3342,7 +3341,10 @@ public class ModelRenderPanel extends JPanel {
// 确保缓冲区大小匹配(可能在 resize 后需要重建) // 确保缓冲区大小匹配(可能在 resize 后需要重建)
if (pixelBuffer == null || pixelInts == null || pixelInts.length != pixelCount) { if (pixelBuffer == null || pixelInts == null || pixelInts.length != pixelCount) {
if (pixelBuffer != null) { if (pixelBuffer != null) {
try { MemoryUtil.memFree(pixelBuffer); } catch (Throwable ignored) {} try {
MemoryUtil.memFree(pixelBuffer);
} catch (Throwable ignored) {
}
} }
pixelBuffer = MemoryUtil.memAlloc(pixelCount * 4); pixelBuffer = MemoryUtil.memAlloc(pixelCount * 4);
pixelBuffer.order(ByteOrder.nativeOrder()); pixelBuffer.order(ByteOrder.nativeOrder());
@@ -3431,6 +3433,7 @@ public class ModelRenderPanel extends JPanel {
/** /**
* 在 GL 上下文线程上异步执行任务 * 在 GL 上下文线程上异步执行任务
*
* @param task 要在 GL 上下文线程中执行的任务 * @param task 要在 GL 上下文线程中执行的任务
* @return CompletableFuture 用于获取任务执行结果 * @return CompletableFuture 用于获取任务执行结果
*/ */
@@ -3464,6 +3467,7 @@ public class ModelRenderPanel extends JPanel {
/** /**
* 在 GL 上下文线程上异步执行任务并返回结果 * 在 GL 上下文线程上异步执行任务并返回结果
*
* @param task 要在 GL 上下文线程中执行的有返回值的任务 * @param task 要在 GL 上下文线程中执行的有返回值的任务
* @return CompletableFuture 用于获取任务执行结果 * @return CompletableFuture 用于获取任务执行结果
*/ */
@@ -3495,6 +3499,7 @@ public class ModelRenderPanel extends JPanel {
/** /**
* 同步在 GL 上下文线程上执行任务(会阻塞当前线程直到任务完成) * 同步在 GL 上下文线程上执行任务(会阻塞当前线程直到任务完成)
*
* @param task 要在 GL 上下文线程中执行的任务 * @param task 要在 GL 上下文线程中执行的任务
* @throws Exception 如果任务执行出错 * @throws Exception 如果任务执行出错
*/ */
@@ -3509,6 +3514,7 @@ public class ModelRenderPanel extends JPanel {
/** /**
* 同步在 GL 上下文线程上执行任务并返回结果(会阻塞当前线程直到任务完成) * 同步在 GL 上下文线程上执行任务并返回结果(会阻塞当前线程直到任务完成)
*
* @param task 要在 GL 上下文线程中执行的有返回值的任务 * @param task 要在 GL 上下文线程中执行的有返回值的任务
* @return 任务执行结果 * @return 任务执行结果
* @throws Exception 如果任务执行出错或超时 * @throws Exception 如果任务执行出错或超时
@@ -3541,7 +3547,7 @@ public class ModelRenderPanel extends JPanel {
/** /**
* 重新设置面板大小 * 重新设置面板大小
* * <p>
* 说明:当 Swing 面板被放大时,需要同时调整离屏 GLFW 窗口像素大小、GL 视口以及重分配像素读取缓冲, * 说明:当 Swing 面板被放大时,需要同时调整离屏 GLFW 窗口像素大小、GL 视口以及重分配像素读取缓冲,
* 否则将把较小分辨率的图像拉伸到更大面板上导致模糊。 * 否则将把较小分辨率的图像拉伸到更大面板上导致模糊。
*/ */
@@ -3637,7 +3643,8 @@ public class ModelRenderPanel extends JPanel {
if (windowId != 0) { if (windowId != 0) {
try { try {
GLFW.glfwDestroyWindow(windowId); GLFW.glfwDestroyWindow(windowId);
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
windowId = 0; windowId = 0;
} }
@@ -3654,7 +3661,8 @@ public class ModelRenderPanel extends JPanel {
// 终止 GLFW注意如果应用中还有其他 GLFW 窗口,这里会影响它们) // 终止 GLFW注意如果应用中还有其他 GLFW 窗口,这里会影响它们)
try { try {
GLFW.glfwTerminate(); GLFW.glfwTerminate();
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
logger.info("OpenGL 资源已清理"); logger.info("OpenGL 资源已清理");
} }

View File

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

View File

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

View File

@@ -1,14 +1,17 @@
package com.chuangzhou.vivid2D.render.awt.util; 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 * @author tzdwindows 7
*/ */
public class OperationHistoryManager { 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> undoStack;
private final LinkedList<OperationRecord> redoStack; private final LinkedList<OperationRecord> redoStack;
@@ -35,6 +38,7 @@ public class OperationHistoryManager {
/** /**
* 获取操作记录管理器实例 * 获取操作记录管理器实例
*
* @return 操作记录管理器实例 * @return 操作记录管理器实例
*/ */
public static OperationHistoryManager getInstance() { public static OperationHistoryManager getInstance() {
@@ -43,8 +47,9 @@ public class OperationHistoryManager {
/** /**
* 注册操作记录器 * 注册操作记录器
*
* @param operationType 操作类型标识 * @param operationType 操作类型标识
* @param recorder 操作记录器 * @param recorder 操作记录器
*/ */
public void registerRecorder(String operationType, OperationRecorder recorder) { public void registerRecorder(String operationType, OperationRecorder recorder) {
recorderMap.put(operationType, recorder); recorderMap.put(operationType, recorder);
@@ -52,8 +57,9 @@ public class OperationHistoryManager {
/** /**
* 记录操作 * 记录操作
*
* @param operationType 操作类型 * @param operationType 操作类型
* @param params 操作参数 * @param params 操作参数
*/ */
public void recordOperation(String operationType, Object... params) { public void recordOperation(String operationType, Object... params) {
if (!enabled) return; if (!enabled) return;
@@ -203,9 +209,20 @@ public class OperationHistoryManager {
this.timestamp = System.currentTimeMillis(); this.timestamp = System.currentTimeMillis();
} }
public String getOperationType() { return operationType; } public String getOperationType() {
public Object[] getParams() { return params; } return operationType;
public String getDescription() { return description; } }
public long getTimestamp() { return timestamp; }
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 operationType 操作类型
* @param action 动作类型record, execute, undo, redo, clear * @param action 动作类型record, execute, undo, redo, clear
* @param params 操作参数 * @param params 操作参数
*/ */
void onOperationEvent(String operationType, String action, Object... 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 * @author tzdwindows 7
*/ */
public interface OperationRecorder { public interface OperationRecorder {
/** /**
* 执行操作(用于重做) * 执行操作(用于重做)
*
* @param params 操作参数 * @param params 操作参数
*/ */
void execute(Object... params); void execute(Object... params);
/** /**
* 撤销操作 * 撤销操作
*
* @param params 操作参数 * @param params 操作参数
*/ */
void undo(Object... params); void undo(Object... params);
/** /**
* 获取操作描述用于UI显示 * 获取操作描述用于UI显示
*
* @return 操作描述 * @return 操作描述
*/ */
String getDescription(); String getDescription();

View File

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

View File

@@ -1,11 +1,11 @@
package com.chuangzhou.vivid2D.render.model; package com.chuangzhou.vivid2D.render.model;
public class AnimationParameter { public class AnimationParameter {
private String id; private final String id;
private float value; private float value;
private float defaultValue; private final float defaultValue;
private float minValue; private final float minValue;
private float maxValue; private final float maxValue;
private boolean changed = false; private boolean changed = false;
public AnimationParameter(String id, float min, float max, float defaultValue) { public AnimationParameter(String id, float min, float max, float defaultValue) {
@@ -24,18 +24,33 @@ public class AnimationParameter {
} }
} }
public boolean hasChanged() { return changed; } public boolean hasChanged() {
public void markClean() { this.changed = false; } 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() { public void reset() {
this.value = defaultValue; this.value = defaultValue;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ import java.util.List;
/** /**
* 姿态数据序列化类 * 姿态数据序列化类
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class PoseData implements Serializable { 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 com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import org.joml.Vector2f; import org.joml.Vector2f;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@@ -98,11 +97,23 @@ public class RotationDeformer extends Deformer {
} }
// Getter/Setter // Getter/Setter
public float getBaseAngle() { return baseAngle; } public float getBaseAngle() {
public void setBaseAngle(float baseAngle) { this.baseAngle = baseAngle; } return baseAngle;
}
public float getAngleRange() { return angleRange; } public void setBaseAngle(float baseAngle) {
public void setAngleRange(float angleRange) { this.angleRange = angleRange; } 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 { try {
String enabledKey = map.get(name + ".enabled"); String enabledKey = map.get(name + ".enabled");
if (enabledKey != null) this.enabled = Boolean.parseBoolean(enabledKey); if (enabledKey != null) this.enabled = Boolean.parseBoolean(enabledKey);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
try { try {
String weightKey = map.get(name + ".weight"); String weightKey = map.get(name + ".weight");
if (weightKey != null) this.weight = Float.parseFloat(weightKey); if (weightKey != null) this.weight = Float.parseFloat(weightKey);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
try { try {
String curKey = map.get(name + ".currentValue"); String curKey = map.get(name + ".currentValue");
if (curKey != null) this.currentValue = Float.parseFloat(curKey); if (curKey != null) this.currentValue = Float.parseFloat(curKey);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
// 清空已有数据 // 清空已有数据
this.vertexDeformations.clear(); 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 com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import org.joml.Vector2f; import org.joml.Vector2f;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@@ -247,7 +246,8 @@ public class WaveDeformer extends Deformer {
public float timeMultiplier = 1.0f; public float timeMultiplier = 1.0f;
public float weight = 1.0f; public float weight = 1.0f;
public WaveConfig() {} public WaveConfig() {
}
public WaveConfig(float amplitude, float frequency, float phase, public WaveConfig(float amplitude, float frequency, float phase,
float waveAngle, float timeMultiplier, float weight) { float waveAngle, float timeMultiplier, float weight) {
@@ -287,24 +287,56 @@ public class WaveDeformer extends Deformer {
timeMultiplier = Float.parseFloat(map.get("timeMultiplier")); timeMultiplier = Float.parseFloat(map.get("timeMultiplier"));
} }
public float getTimeMultiplier() { return timeMultiplier; } public float getTimeMultiplier() {
public void setTimeMultiplier(float timeMultiplier) { this.timeMultiplier = timeMultiplier; } return timeMultiplier;
}
public ControlMode getControlMode() { return controlMode; } public void setTimeMultiplier(float timeMultiplier) {
this.timeMultiplier = timeMultiplier;
}
public ControlMode getControlMode() {
return controlMode;
}
// Getter/Setter // Getter/Setter
public float getAmplitude() { return amplitude; } public float getAmplitude() {
public void setAmplitude(float amplitude) { this.amplitude = amplitude; } return amplitude;
}
public float getFrequency() { return frequency; } public void setAmplitude(float amplitude) {
public void setFrequency(float frequency) { this.frequency = frequency; } this.amplitude = amplitude;
}
public float getPhase() { return phase; } public float getFrequency() {
public void setPhase(float phase) { this.phase = phase; } return frequency;
}
public float getWaveAngle() { return waveAngle; } public void setFrequency(float frequency) {
public void setWaveAngle(float waveAngle) { this.waveAngle = waveAngle; } this.frequency = frequency;
}
public float getCurrentTime() { return currentTime; } public float getPhase() {
public void setCurrentTime(float currentTime) { this.currentTime = currentTime; } 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 float currentTime;
private boolean playing; private boolean playing;
private boolean paused; private boolean paused;
private Map<String, Float> parameterOverrides; private final Map<String, Float> parameterOverrides;
// ==================== 事件系统 ==================== // ==================== 事件系统 ====================
private final List<AnimationEventListener> eventListeners; private final List<AnimationEventListener> eventListeners;
@@ -468,22 +468,45 @@ public class AnimationLayer {
// ==================== Getter/Setter ==================== // ==================== Getter/Setter ====================
public String getName() { return name; } public String getName() {
public UUID getUuid() { return uuid; } return name;
}
public UUID getUuid() {
return uuid;
}
public float getWeight() {
return weight;
}
public float getWeight() { return weight; }
public void setWeight(float weight) { public void setWeight(float weight) {
this.weight = Math.max(0.0f, Math.min(1.0f, weight)); this.weight = Math.max(0.0f, Math.min(1.0f, weight));
} }
public boolean isEnabled() { return enabled; } public boolean isEnabled() {
public void setEnabled(boolean enabled) { this.enabled = enabled; } return enabled;
}
public BlendMode getBlendMode() { return blendMode; } public void setEnabled(boolean enabled) {
public void setBlendMode(BlendMode blendMode) { this.blendMode = blendMode; } this.enabled = enabled;
}
public int getPriority() { return priority; } public BlendMode getBlendMode() {
public void setPriority(int priority) { this.priority = priority; } 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() { public Map<String, AnimationTrack> getTracks() {
return Collections.unmodifiableMap(tracks); return Collections.unmodifiableMap(tracks);
@@ -493,20 +516,37 @@ public class AnimationLayer {
return Collections.unmodifiableList(clips); 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) { public void setPlaybackSpeed(float playbackSpeed) {
this.playbackSpeed = Math.max(0.0f, playbackSpeed); this.playbackSpeed = Math.max(0.0f, playbackSpeed);
} }
public boolean isLooping() { return looping; } public boolean isLooping() {
public void setLooping(boolean looping) { this.looping = looping; } return looping;
}
public float getCurrentTime() { return currentTime; } public void setLooping(boolean looping) {
this.looping = looping;
}
public boolean isPlaying() { return playing; } public float getCurrentTime() {
public boolean isPaused() { return paused; } return currentTime;
}
public boolean isPlaying() {
return playing;
}
public boolean isPaused() {
return paused;
}
public Map<String, Float> getParameterOverrides() { public Map<String, Float> getParameterOverrides() {
return Collections.unmodifiableMap(parameterOverrides); return Collections.unmodifiableMap(parameterOverrides);
@@ -616,12 +656,29 @@ public class AnimationLayer {
} }
// Getter/Setter // Getter/Setter
public String getParameterId() { return parameterId; } public String getParameterId() {
public List<Keyframe> getKeyframes() { return Collections.unmodifiableList(keyframes); } return parameterId;
public boolean isEnabled() { return enabled; } }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public InterpolationType getInterpolation() { return interpolation; } public List<Keyframe> getKeyframes() {
public void setInterpolation(InterpolationType interpolation) { this.interpolation = interpolation; } 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 // Getter
public float getTime() { return time; } public float getTime() {
public float getValue() { return value; } return time;
public InterpolationType getInterpolation() { return interpolation; } }
public float getValue() {
return value;
}
public InterpolationType getInterpolation() {
return interpolation;
}
} }
/** /**
@@ -687,9 +752,17 @@ public class AnimationLayer {
} }
// Getter // Getter
public String getName() { return name; } public String getName() {
public float getTime() { return time; } return name;
public boolean isTriggered() { return triggered; } }
public float getTime() {
return time;
}
public boolean isTriggered() {
return triggered;
}
} }
/** /**
@@ -697,11 +770,17 @@ public class AnimationLayer {
*/ */
public interface AnimationEventListener { public interface AnimationEventListener {
void onAnimationStarted(AnimationLayer layer, AnimationClip clip); void onAnimationStarted(AnimationLayer layer, AnimationClip clip);
void onAnimationStopped(AnimationLayer layer, AnimationClip clip); void onAnimationStopped(AnimationLayer layer, AnimationClip clip);
void onAnimationPaused(AnimationLayer layer, AnimationClip clip); void onAnimationPaused(AnimationLayer layer, AnimationClip clip);
void onAnimationResumed(AnimationLayer layer, AnimationClip clip); void onAnimationResumed(AnimationLayer layer, AnimationClip clip);
void onAnimationCompleted(AnimationLayer layer, AnimationClip clip); void onAnimationCompleted(AnimationLayer layer, AnimationClip clip);
void onAnimationLooped(AnimationLayer layer, AnimationClip clip); void onAnimationLooped(AnimationLayer layer, AnimationClip clip);
void onEventTriggered(AnimationLayer layer, AnimationEvent event); void onEventTriggered(AnimationLayer layer, AnimationEvent event);
} }
@@ -709,13 +788,33 @@ public class AnimationLayer {
* 简单的动画事件监听器适配器 * 简单的动画事件监听器适配器
*/ */
public static abstract class AnimationEventAdapter implements AnimationEventListener { public static abstract class AnimationEventAdapter implements AnimationEventListener {
@Override public void onAnimationStarted(AnimationLayer layer, AnimationClip clip) {} @Override
@Override public void onAnimationStopped(AnimationLayer layer, AnimationClip clip) {} public void onAnimationStarted(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
@Override public void onAnimationLooped(AnimationLayer layer, AnimationClip clip) {} public void onAnimationStopped(AnimationLayer layer, AnimationClip clip) {
@Override public void onEventTriggered(AnimationLayer layer, AnimationEvent event) {} }
@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 方法 ==================== // ==================== Object 方法 ====================

View File

@@ -229,12 +229,13 @@ public abstract class Deformer {
* 变形范围控制 * 变形范围控制
*/ */
public static class DeformationRange { 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 radius = 100.0f;
private float innerRadius = 0.0f; private float innerRadius = 0.0f;
private float falloff = 2.0f; private float falloff = 2.0f;
public DeformationRange() {} public DeformationRange() {
}
public DeformationRange(Vector2f center, float radius) { public DeformationRange(Vector2f center, float radius) {
this.center.set(center); this.center.set(center);
@@ -263,17 +264,40 @@ public abstract class Deformer {
} }
// Getter/Setter // Getter/Setter
public Vector2f getCenter() { return new Vector2f(center); } public Vector2f getCenter() {
public void setCenter(Vector2f center) { this.center.set(center); } return new Vector2f(center);
public void setCenter(float x, float y) { this.center.set(x, y); } }
public float getRadius() { return radius; } public void setCenter(Vector2f center) {
public void setRadius(float radius) { this.radius = radius; } this.center.set(center);
}
public float getInnerRadius() { return innerRadius; } public void setCenter(float x, float y) {
public void setInnerRadius(float innerRadius) { this.innerRadius = innerRadius; } this.center.set(x, y);
}
public float getFalloff() { return falloff; } public float getRadius() {
public void setFalloff(float falloff) { this.falloff = falloff; } 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 * @author tzdwindows 7
*/ */
public class LightSource { public class LightSource {
private Vector2f position; private final Vector2f position;
private Vector3f color; private final Vector3f color;
private float intensity; private final float intensity;
private boolean enabled = true; private boolean enabled = true;
private boolean isAmbient = false; // 是否为环境光 private boolean isAmbient = false; // 是否为环境光
@@ -73,31 +74,73 @@ public class LightSource {
return new Color(red, green, blue); return new Color(red, green, blue);
} }
public Vector2f getPosition() { return position; } public Vector2f getPosition() {
public Vector3f getColor() { return color; } return position;
public float getIntensity() { return intensity; } }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; } 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 boolean isAmbient() {
public void setAmbient(boolean ambient) { this.isAmbient = ambient; } return isAmbient;
}
public void setAmbient(boolean ambient) {
this.isAmbient = ambient;
}
// ---- 辉光相关的 getter / setter ---- // ---- 辉光相关的 getter / setter ----
public boolean isGlow() { return isGlow; } public boolean isGlow() {
public void setGlow(boolean glow) { this.isGlow = glow; } 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) { public void setGlowDirection(Vector2f glowDirection) {
this.glowDirection = glowDirection != null ? glowDirection : new Vector2f(0f, 0f); this.glowDirection = glowDirection != null ? glowDirection : new Vector2f(0f, 0f);
} }
public float getGlowIntensity() { return glowIntensity; } public float getGlowIntensity() {
public void setGlowIntensity(float glowIntensity) { this.glowIntensity = glowIntensity; } return glowIntensity;
}
public float getGlowRadius() { return glowRadius; } public void setGlowIntensity(float glowIntensity) {
public void setGlowRadius(float glowRadius) { this.glowRadius = glowRadius; } this.glowIntensity = glowIntensity;
}
public float getGlowAmount() { return glowAmount; } public float getGlowRadius() {
public void setGlowAmount(float glowAmount) { this.glowAmount = glowAmount; } 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 { public static class PartPose {
private Vector2f position; private final Vector2f position;
private float rotation; private float rotation;
private Vector2f scale; private final Vector2f scale;
private float opacity; private float opacity;
private boolean visible; private boolean visible;
private Vector3f color; // RGB颜色乘数 private final Vector3f color; // RGB颜色乘数
public PartPose() { public PartPose() {
this(new Vector2f(0, 0), 0.0f, new Vector2f(1, 1), 1.0f, true, new Vector3f(1, 1, 1)); 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 ================== // ================== Getter和Setter ==================
public Vector2f getPosition() { return new Vector2f(position); } public Vector2f getPosition() {
public void setPosition(Vector2f position) { this.position.set(position); } return new Vector2f(position);
}
public float getRotation() { return rotation; } public void setPosition(Vector2f position) {
public void setRotation(float rotation) { this.rotation = rotation; } this.position.set(position);
}
public Vector2f getScale() { return new Vector2f(scale); } public float getRotation() {
public void setScale(Vector2f scale) { this.scale.set(scale); } return rotation;
}
public float getOpacity() { return opacity; } public void setRotation(float rotation) {
public void setOpacity(float opacity) { this.opacity = opacity; } this.rotation = rotation;
}
public boolean isVisible() { return visible; } public Vector2f getScale() {
public void setVisible(boolean visible) { this.visible = visible; } return new Vector2f(scale);
}
public Vector3f getColor() { return new Vector3f(color); } public void setScale(Vector2f scale) {
public void setColor(Vector3f color) { this.color.set(color); } 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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
@@ -357,14 +387,29 @@ public class ModelPose {
// ================== Getter和Setter ================== // ================== Getter和Setter ==================
public String getName() { return name; } public String getName() {
public void setName(String name) { this.name = name; } return name;
}
public float getBlendTime() { return blendTime; } public void setName(String name) {
public void setBlendTime(float blendTime) { this.blendTime = Math.max(0, blendTime); } this.name = name;
}
public boolean isDefaultPose() { return isDefaultPose; } public float getBlendTime() {
public void setDefaultPose(boolean defaultPose) { isDefaultPose = defaultPose; } 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 static int NEXT_ID = 0;
private int id; private int id;
private Vector2f position; private final Vector2f position;
private Vector2f originalPosition; private final Vector2f originalPosition;
private Vector2f uv; private final Vector2f uv;
private float influenceRadius = 100.0f; private float influenceRadius = 100.0f;
private boolean selected = false; private boolean selected = false;
private String name; 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) { public PuppetPin(float x, float y, float u, float v) {
this.id = NEXT_ID++; this.id = NEXT_ID++;
@@ -97,18 +97,53 @@ public class PuppetPin {
// ==================== 原有方法 ==================== // ==================== 原有方法 ====================
// Getters and Setters // Getters and Setters
public Vector2f getPosition() { return new Vector2f(position); } public Vector2f getPosition() {
public void setPosition(float x, float y) { this.position.set(x, y); } return new Vector2f(position);
public void setPosition(Vector2f pos) { this.position.set(pos); } }
public Vector2f getOriginalPosition() { return new Vector2f(originalPosition); }
public float getInfluenceRadius() { return influenceRadius; } public void setPosition(float x, float y) {
public void setInfluenceRadius(float radius) { this.influenceRadius = radius; } this.position.set(x, y);
public boolean isSelected() { return selected; } }
public void setSelected(boolean selected) { this.selected = selected; }
public int getId() { return id; } public void setPosition(Vector2f pos) {
public String getName() { return name; } this.position.set(pos);
public void setName(String name) { this.name = name; } }
public Map<Integer, Float> getWeightMap() { return new HashMap<>(weightMap); }
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) { public void move(float dx, float dy) {
position.add(dx, dy); position.add(dx, dy);

View File

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

View File

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

View File

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

View File

@@ -11,20 +11,19 @@ import java.nio.FloatBuffer;
/** /**
* 简化版 BufferBuilder用于按顶点流构建并一次性绘制几何体。 * 简化版 BufferBuilder用于按顶点流构建并一次性绘制几何体。
* 每个顶点格式: float x, float y, float u, float v 共4个 float * 每个顶点格式: float x, float y, float u, float v 共4个 float
* * <p>
* 用法: * 用法:
* BufferBuilder bb = new BufferBuilder(); * BufferBuilder bb = new BufferBuilder();
* bb.begin(GL11.GL_LINE_LOOP, 16); * bb.begin(GL11.GL_LINE_LOOP, 16);
* bb.vertex(x,y,u,v); * bb.vertex(x,y,u,v);
* ... * ...
* bb.end(); // 立即绘制并 cleanup * bb.end(); // 立即绘制并 cleanup
* * <p>
* 设计原则:简单、可靠、方便把临时多顶点数据提交到 GPU。 * 设计原则:简单、可靠、方便把临时多顶点数据提交到 GPU。
* *
* * @author tzdwindows
* @version 1.2 * @version 1.2
* @since 2025-10-16 * @since 2025-10-16
* @author tzdwindows
*/ */
public class BufferBuilder { public class BufferBuilder {
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BufferBuilder.class); 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 size; // float 数量
private int vertexCount; private int vertexCount;
private int mode; // GL mode private int mode; // GL mode
private RenderState renderState = new RenderState(); private final RenderState renderState = new RenderState();
private boolean stateSaved = false; private boolean stateSaved = false;
// 内置状态,用于构建完成的缓冲区 // 内置状态,用于构建完成的缓冲区
public static class BuiltBuffer { public static class BuiltBuffer {
private final int vao; private final int vao;
@@ -51,12 +51,25 @@ public class BufferBuilder {
this.renderState = renderState; this.renderState = renderState;
} }
public RenderState renderState() { return renderState; } public RenderState renderState() {
return renderState;
}
public int vao() { return vao; } public int vao() {
public int vbo() { return vbo; } return vao;
public int vertexCount() { return vertexCount; } }
public int mode() { return mode; }
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 * @author tzdwindows 7
* @version 1.1 - 添加颜色支持
*/ */
public class BufferUploader { public class BufferUploader {
@@ -65,7 +65,8 @@ public class BufferUploader {
} }
if (currentProgram != 0) { if (currentProgram != 0) {
int colorLoc = RenderSystem.getUniformLocation(currentProgram, "uColor"); int colorLoc = RenderSystem.getUniformLocation(currentProgram, "uColor");
if (colorLoc == -1) {} else { if (colorLoc == -1) {
} else {
RenderSystem.uniform4f(colorLoc, RenderSystem.uniform4f(colorLoc,
state.color.x, state.color.y, state.color.z, state.color.w); 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 * @author tzdwindows
* @version 1.0
*/ */
public class Tesselator { public class Tesselator {
private static final int DEFAULT_BUFFER_SIZE = 2097152; // 2MB 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 * @author tzdwindows 7
*/ */
public interface CompleteShader { public interface CompleteShader {
Shader getVertexShader(); Shader getVertexShader();
Shader getFragmentShader(); Shader getFragmentShader();
String getShaderName(); String getShaderName();
boolean isDefaultShader(); 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 * @author tzdwindows 7
*/ */
public interface Shader { public interface Shader {
String getShaderCode(); String getShaderCode();
String getShaderName(); String getShaderName();
} }

View File

@@ -8,104 +8,105 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
public class FragmentShaders implements Shader { public class FragmentShaders implements Shader {
public static final String FRAGMENT_SHADER_SRC = public static final String FRAGMENT_SHADER_SRC =
""" """
#version 330 core #version 330 core
in vec2 vTexCoord; in vec2 vTexCoord;
in vec2 vWorldPos; in vec2 vWorldPos;
out vec4 FragColor; out vec4 FragColor;
uniform sampler2D uTexture; uniform sampler2D uTexture;
uniform vec4 uColor; uniform vec4 uColor;
uniform float uOpacity; uniform float uOpacity;
uniform int uBlendMode; uniform int uBlendMode;
uniform int uDebugMode; uniform int uDebugMode;
#define MAX_LIGHTS 8 #define MAX_LIGHTS 8
uniform vec2 uLightsPos[MAX_LIGHTS]; uniform vec2 uLightsPos[MAX_LIGHTS];
uniform vec3 uLightsColor[MAX_LIGHTS]; uniform vec3 uLightsColor[MAX_LIGHTS];
uniform float uLightsIntensity[MAX_LIGHTS]; uniform float uLightsIntensity[MAX_LIGHTS];
uniform int uLightsIsAmbient[MAX_LIGHTS]; uniform int uLightsIsAmbient[MAX_LIGHTS];
uniform int uLightCount; uniform int uLightCount;
// 常用衰减系数(可在 shader 内微调) // 常用衰减系数(可在 shader 内微调)
const float ATT_CONST = 1.0; const float ATT_CONST = 1.0;
const float ATT_LINEAR = 0.09; const float ATT_LINEAR = 0.09;
const float ATT_QUAD = 0.032; const float ATT_QUAD = 0.032;
void main() { void main() {
// 先采样纹理 // 先采样纹理
vec4 tex = texture(uTexture, vTexCoord); vec4 tex = texture(uTexture, vTexCoord);
float alpha = tex.a * uOpacity; float alpha = tex.a * uOpacity;
if (alpha <= 0.001) discard; if (alpha <= 0.001) discard;
// 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色) // 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色)
if (uLightCount == 0) { if (uLightCount == 0) {
vec3 base = tex.rgb * uColor.rgb; vec3 base = tex.rgb * uColor.rgb;
// 简单的色调映射(防止数值过大) // 简单的色调映射(防止数值过大)
base = clamp(base, 0.0, 1.0); base = clamp(base, 0.0, 1.0);
FragColor = vec4(base, alpha); FragColor = vec4(base, alpha);
return; return;
} }
// 基础颜色(纹理 * 部件颜色) // 基础颜色(纹理 * 部件颜色)
vec3 baseColor = tex.rgb * uColor.rgb; vec3 baseColor = tex.rgb * uColor.rgb;
// 全局环境光基线(可以适度提高以避免全黑) // 全局环境光基线(可以适度提高以避免全黑)
vec3 ambient = vec3(0.06); // 小环境光补偿 vec3 ambient = vec3(0.06); // 小环境光补偿
vec3 lighting = vec3(0.0); vec3 lighting = vec3(0.0);
vec3 specularAccum = vec3(0.0); vec3 specularAccum = vec3(0.0);
// 累积环境光(来自被标记为环境光的光源) // 累积环境光(来自被标记为环境光的光源)
for (int i = 0; i < uLightCount; ++i) { for (int i = 0; i < uLightCount; ++i) {
if (uLightsIsAmbient[i] == 1) { if (uLightsIsAmbient[i] == 1) {
lighting += uLightsColor[i] * uLightsIntensity[i]; 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 @Override
public String getShaderCode() { public String getShaderCode() {
return FRAGMENT_SHADER_SRC; return FRAGMENT_SHADER_SRC;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,11 +16,11 @@ import static org.lwjgl.opengl.GL11.*;
/** /**
* ModelLoadTest - enhanced debug loader * ModelLoadTest - enhanced debug loader
* * <p>
* - 加载模型后会自动检查 model 内部结构并打印parts, meshes, textures * - 加载模型后会自动检查 model 内部结构并打印parts, meshes, textures
* - 尝试把第一个 part 放到窗口中心以确保在可视范围内 * - 尝试把第一个 part 放到窗口中心以确保在可视范围内
* - 每帧确保 ModelRender 的 viewport 与窗口大小一致 * - 每帧确保 ModelRender 的 viewport 与窗口大小一致
* * <p>
* 运行前请确保 MODEL_PATH 指向你保存的 model 文件 * 运行前请确保 MODEL_PATH 指向你保存的 model 文件
*/ */
public class ModelLoadTest { public class ModelLoadTest {
@@ -115,7 +115,7 @@ public class ModelLoadTest {
* 其次尝试创建空实例并调用实例方法 loadFromFile(String) * 其次尝试创建空实例并调用实例方法 loadFromFile(String)
*/ */
private void loadModelFromFile(String path) { private void loadModelFromFile(String path) {
inspectSerializedFileStructure( path); inspectSerializedFileStructure(path);
File f = new File(path); File f = new File(path);
if (!f.exists()) { if (!f.exists()) {
System.err.println("Model file not found: " + path); System.err.println("Model file not found: " + path);
@@ -165,7 +165,8 @@ public class ModelLoadTest {
model = inst; model = inst;
System.out.println("Model loaded via instance method loadFromFile"); System.out.println("Model loaded via instance method loadFromFile");
return; return;
} catch (NoSuchMethodException ignored) { } } catch (NoSuchMethodException ignored) {
}
} }
} catch (Throwable t) { } catch (Throwable t) {
// ignore // ignore
@@ -209,35 +210,50 @@ public class ModelLoadTest {
private void printObjectStructure(Object obj, int indent, java.util.Set<Object> seen) { private void printObjectStructure(Object obj, int indent, java.util.Set<Object> seen) {
if (obj == null) { if (obj == null) {
printIndent(indent); System.out.println("null"); return; printIndent(indent);
System.out.println("null");
return;
} }
if (seen.contains(obj)) { 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); seen.add(obj);
Class<?> cls = obj.getClass(); Class<?> cls = obj.getClass();
printIndent(indent); System.out.println(cls.getName()); printIndent(indent);
System.out.println(cls.getName());
// 如果是集合,列出元素类型/数目(不深入过深避免堆栈) // 如果是集合,列出元素类型/数目(不深入过深避免堆栈)
if (obj instanceof java.util.Collection) { if (obj instanceof java.util.Collection col) {
java.util.Collection col = (java.util.Collection) obj; printIndent(indent + 1);
printIndent(indent+1); System.out.println("size=" + col.size()); System.out.println("size=" + col.size());
int i = 0; int i = 0;
for (Object e : col) { for (Object e : col) {
if (i++ > 20) { printIndent(indent+1); System.out.println("... (truncated)"); break; } if (i++ > 20) {
printObjectStructure(e, indent+1, seen); printIndent(indent + 1);
System.out.println("... (truncated)");
break;
}
printObjectStructure(e, indent + 1, seen);
} }
return; return;
} }
if (obj instanceof java.util.Map) { if (obj instanceof java.util.Map map) {
java.util.Map map = (java.util.Map) obj; printIndent(indent + 1);
printIndent(indent+1); System.out.println("size=" + map.size()); System.out.println("size=" + map.size());
int i = 0; int i = 0;
for (Object k : map.keySet()) { for (Object k : map.keySet()) {
if (i++ > 20) { printIndent(indent+1); System.out.println("... (truncated)"); break; } if (i++ > 20) {
printIndent(indent+1); System.out.println("Key:"); printIndent(indent + 1);
printObjectStructure(k, indent+2, seen); System.out.println("... (truncated)");
printIndent(indent+1); System.out.println("Value:"); break;
printObjectStructure(map.get(k), indent+2, seen); }
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; return;
} }
@@ -246,12 +262,16 @@ public class ModelLoadTest {
for (java.lang.reflect.Field f : fields) { for (java.lang.reflect.Field f : fields) {
f.setAccessible(true); f.setAccessible(true);
Object val = null; Object val = null;
try { val = f.get(obj); } catch (Throwable ignored) {} try {
printIndent(indent+1); System.out.println(f.getName() + " : " + (val == null ? "null" : val.getClass().getName())); 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 != null && !val.getClass().getName().startsWith("java.") && !val.getClass().isPrimitive()) {
if (val instanceof Number || val instanceof String) continue; 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) { if (getParts != null) {
Object partsObj = getParts.invoke(model); Object partsObj = getParts.invoke(model);
if (partsObj instanceof List) { if (partsObj instanceof List<?> parts) {
List<?> parts = (List<?>) partsObj;
System.out.println("Parts count: " + parts.size()); System.out.println("Parts count: " + parts.size());
for (int i = 0; i < parts.size(); i++) { for (int i = 0; i < parts.size(); i++) {
Object p = parts.get(i); Object p = parts.get(i);
@@ -296,7 +315,8 @@ public class ModelLoadTest {
Object name = getName != null ? getName.invoke(p) : "<no-name>"; Object name = getName != null ? getName.invoke(p) : "<no-name>";
Object pos = getPosition != null ? getPosition.invoke(p) : null; Object pos = getPosition != null ? getPosition.invoke(p) : null;
System.out.println(" name=" + name + ", pos=" + pos); System.out.println(" name=" + name + ", pos=" + pos);
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
} }
// 如果 parts 不为空,尝试把第一个 part 放到窗口中心(如果有 setPosition // 如果 parts 不为空,尝试把第一个 part 放到窗口中心(如果有 setPosition
@@ -305,7 +325,7 @@ public class ModelLoadTest {
try { try {
Method setPosition = tryGetMethod(first.getClass(), "setPosition", float.class, float.class); Method setPosition = tryGetMethod(first.getClass(), "setPosition", float.class, float.class);
if (setPosition != null) { 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."); System.out.println("Moved first part to window center.");
} }
} catch (Throwable t) { } catch (Throwable t) {
@@ -317,8 +337,7 @@ public class ModelLoadTest {
if (getMeshes != null) { if (getMeshes != null) {
Object meshesObj = getMeshes.invoke(model); Object meshesObj = getMeshes.invoke(model);
if (meshesObj instanceof List) { if (meshesObj instanceof List<?> meshes) {
List<?> meshes = (List<?>) meshesObj;
System.out.println("Meshes count: " + meshes.size()); System.out.println("Meshes count: " + meshes.size());
for (int i = 0; i < Math.min(meshes.size(), 10); i++) { for (int i = 0; i < Math.min(meshes.size(), 10); i++) {
Object m = meshes.get(i); Object m = meshes.get(i);
@@ -330,15 +349,15 @@ public class ModelLoadTest {
Object vc = getVertexCount != null ? getVertexCount.invoke(m) : null; Object vc = getVertexCount != null ? getVertexCount.invoke(m) : null;
Object tex = getTexture != null ? getTexture.invoke(m) : null; Object tex = getTexture != null ? getTexture.invoke(m) : null;
System.out.println(" vertexCount=" + vc + ", texture=" + tex); System.out.println(" vertexCount=" + vc + ", texture=" + tex);
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
} }
} }
} }
if (getTextures != null) { if (getTextures != null) {
Object texObj = getTextures.invoke(model); Object texObj = getTextures.invoke(model);
if (texObj instanceof List) { if (texObj instanceof List<?> texs) {
List<?> texs = (List<?>) texObj;
System.out.println("Textures count: " + texs.size()); System.out.println("Textures count: " + texs.size());
for (int i = 0; i < Math.min(texs.size(), 10); i++) { for (int i = 0; i < Math.min(texs.size(), 10); i++) {
Object t = texs.get(i); Object t = texs.get(i);
@@ -351,7 +370,8 @@ public class ModelLoadTest {
Object h = getH != null ? getH.invoke(t) : "?"; Object h = getH != null ? getH.invoke(t) : "?";
Object id = getId != null ? getId.invoke(t) : "?"; Object id = getId != null ? getId.invoke(t) : "?";
System.out.println(" size=" + w + "x" + h + ", id=" + id); System.out.println(" size=" + w + "x" + h + ", id=" + id);
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
} }
} }
} }
@@ -437,8 +457,8 @@ public class ModelLoadTest {
try { try {
ModelRender.cleanup(); ModelRender.cleanup();
} catch (Throwable ignored) {} } catch (Throwable ignored) {
}
if (window != MemoryUtil.NULL) { 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.model.util.Texture;
import com.chuangzhou.vivid2D.render.systems.RenderSystem; import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import org.joml.Vector2f; import org.joml.Vector2f;
import org.joml.Vector3f;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.glfw.GLFWVidMode;
@@ -22,6 +21,7 @@ import java.util.Random;
/** /**
* ModelRenderLightingTest * ModelRenderLightingTest
* 测试使用 Model2D + 光源进行简单光照渲染 * 测试使用 Model2D + 光源进行简单光照渲染
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class ModelRenderLightingTest { public class ModelRenderLightingTest {
@@ -34,7 +34,7 @@ public class ModelRenderLightingTest {
private boolean running = true; private boolean running = true;
private Model2D model; private Model2D model;
private Random random = new Random(); private final Random random = new Random();
private float animationTime = 0f; private float animationTime = 0f;
@@ -115,7 +115,7 @@ public class ModelRenderLightingTest {
rightArm.setPosition(60, -20); rightArm.setPosition(60, -20);
Mesh2D rightArmMesh = Mesh2D.createQuad("right_arm_mesh", 18, 90); Mesh2D rightArmMesh = Mesh2D.createQuad("right_arm_mesh", 18, 90);
rightArmMesh.setTexture(createSolidTexture(16, 90, 0xFF6495ED)); rightArmMesh.setTexture(createSolidTexture(16, 90, 0xFF6495ED));
rightArmMesh.setSelected( true); rightArmMesh.setSelected(true);
rightArm.addMesh(rightArmMesh); rightArm.addMesh(rightArmMesh);
// legs // legs
@@ -152,11 +152,11 @@ public class ModelRenderLightingTest {
private Texture createSolidTexture(int w, int h, int rgba) { private Texture createSolidTexture(int w, int h, int rgba) {
ByteBuffer buf = MemoryUtil.memAlloc(w * h * 4); ByteBuffer buf = MemoryUtil.memAlloc(w * h * 4);
byte a = (byte)((rgba >> 24) & 0xFF); byte a = (byte) ((rgba >> 24) & 0xFF);
byte r = (byte)((rgba >> 16) & 0xFF); byte r = (byte) ((rgba >> 16) & 0xFF);
byte g = (byte)((rgba >> 8) & 0xFF); byte g = (byte) ((rgba >> 8) & 0xFF);
byte b = (byte)(rgba & 0xFF); byte b = (byte) (rgba & 0xFF);
for(int i=0;i<w*h;i++) buf.put(r).put(g).put(b).put(a); for (int i = 0; i < w * h; i++) buf.put(r).put(g).put(b).put(a);
buf.flip(); buf.flip();
Texture t = new Texture("solid_" + rgba, w, h, Texture.TextureFormat.RGBA, buf); Texture t = new Texture("solid_" + rgba, w, h, Texture.TextureFormat.RGBA, buf);
MemoryUtil.memFree(buf); MemoryUtil.memFree(buf);
@@ -166,16 +166,16 @@ public class ModelRenderLightingTest {
private Texture createHeadTexture() { private Texture createHeadTexture() {
int width = 64, height = 64; int width = 64, height = 64;
int[] pixels = new int[width * height]; int[] pixels = new int[width * height];
for(int y=0;y<height;y++) { for (int y = 0; y < height; y++) {
for(int x=0;x<width;x++) { for (int x = 0; x < width; x++) {
float dx = (x - width/2f)/(width/2f); float dx = (x - width / 2f) / (width / 2f);
float dy = (y - height/2f)/(height/2f); float dy = (y - height / 2f) / (height / 2f);
float dist = (float)Math.sqrt(dx*dx + dy*dy); float dist = (float) Math.sqrt(dx * dx + dy * dy);
int alpha = dist>1.0f?0:255; int alpha = dist > 1.0f ? 0 : 255;
int r = (int)(240*(1f - dist*0.25f)); int r = (int) (240 * (1f - dist * 0.25f));
int g = (int)(200*(1f - dist*0.25f)); int g = (int) (200 * (1f - dist * 0.25f));
int b = (int)(180*(1f - dist*0.25f)); int b = (int) (180 * (1f - dist * 0.25f));
pixels[y*width + x] = (alpha<<24)|(r<<16)|(g<<8)|b; pixels[y * width + x] = (alpha << 24) | (r << 16) | (g << 8) | b;
} }
} }
return new Texture("head_tex", width, height, Texture.TextureFormat.RGBA, pixels); return new Texture("head_tex", width, height, Texture.TextureFormat.RGBA, pixels);
@@ -188,11 +188,11 @@ public class ModelRenderLightingTest {
while (running && !GLFW.glfwWindowShouldClose(window)) { while (running && !GLFW.glfwWindowShouldClose(window)) {
long now = System.nanoTime(); long now = System.nanoTime();
accumulator += (now - last)/nsPerUpdate; accumulator += (now - last) / nsPerUpdate;
last = now; last = now;
while (accumulator >= 1.0) { while (accumulator >= 1.0) {
update(1.0f/60.0f); update(1.0f / 60.0f);
accumulator -= 1.0; accumulator -= 1.0;
} }
@@ -204,9 +204,9 @@ public class ModelRenderLightingTest {
private void update(float dt) { private void update(float dt) {
animationTime += dt; animationTime += dt;
float armSwing = (float)Math.sin(animationTime*3f)*0.7f; float armSwing = (float) Math.sin(animationTime * 3f) * 0.7f;
float legSwing = (float)Math.sin(animationTime*3f + Math.PI)*0.6f; float legSwing = (float) Math.sin(animationTime * 3f + Math.PI) * 0.6f;
float headRot = (float)Math.sin(animationTime*1.4f)*0.15f; float headRot = (float) Math.sin(animationTime * 1.4f) * 0.15f;
ModelPart leftArm = model.getPart("left_arm"); ModelPart leftArm = model.getPart("left_arm");
ModelPart rightArm = model.getPart("right_arm"); ModelPart rightArm = model.getPart("right_arm");
@@ -214,24 +214,24 @@ public class ModelRenderLightingTest {
ModelPart rightLeg = model.getPart("right_leg"); ModelPart rightLeg = model.getPart("right_leg");
ModelPart head = model.getPart("head"); ModelPart head = model.getPart("head");
if(leftArm!=null) leftArm.setRotation(-0.8f*armSwing - 0.2f); if (leftArm != null) leftArm.setRotation(-0.8f * armSwing - 0.2f);
if(rightArm!=null) rightArm.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 (leftLeg != null) leftLeg.setRotation(0.6f * legSwing);
if(rightLeg!=null) rightLeg.setRotation(-0.6f*legSwing); if (rightLeg != null) rightLeg.setRotation(-0.6f * legSwing);
if(head!=null) head.setRotation(headRot); if (head != null) head.setRotation(headRot);
model.update(dt); model.update(dt);
} }
private void render() { private void render() {
RenderSystem.setClearColor(0.18f,0.18f,0.25f,1.0f); RenderSystem.setClearColor(0.18f, 0.18f, 0.25f, 1.0f);
ModelRender.render(1f/60f, model); ModelRender.render(1f / 60f, model);
} }
private void cleanup() { private void cleanup() {
ModelRender.cleanup(); ModelRender.cleanup();
Texture.cleanupAll(); Texture.cleanupAll();
if(window!= MemoryUtil.NULL) GLFW.glfwDestroyWindow(window); if (window != MemoryUtil.NULL) GLFW.glfwDestroyWindow(window);
GLFW.glfwTerminate(); GLFW.glfwTerminate();
GLFW.glfwSetErrorCallback(null).free(); GLFW.glfwSetErrorCallback(null).free();
} }

View File

@@ -21,7 +21,6 @@ import java.util.Random;
* 重写后的 ModelRender 测试示例:构造一个简单的人形(头、身体、左右手、左右腿) * 重写后的 ModelRender 测试示例:构造一个简单的人形(头、身体、左右手、左右腿)
* 便于验证层级变换与渲染是否正确。 * 便于验证层级变换与渲染是否正确。
* *
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class ModelRenderTest { public class ModelRenderTest {
@@ -34,7 +33,7 @@ public class ModelRenderTest {
private boolean running = true; private boolean running = true;
private Model2D testModel; private Model2D testModel;
private Random random = new Random(); private final Random random = new Random();
private float animationTime = 0f; private float animationTime = 0f;
private boolean animate = true; 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.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.Texture; import com.chuangzhou.vivid2D.render.model.util.Texture;
import com.chuangzhou.vivid2D.render.systems.RenderSystem; import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import org.joml.Matrix3f;
import org.joml.Vector2f;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWErrorCallback; import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.glfw.GLFWVidMode;
@@ -18,6 +16,7 @@ import java.nio.ByteBuffer;
/** /**
* 用于测试中心点旋转 * 用于测试中心点旋转
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class ModelRenderTest2 { public class ModelRenderTest2 {
@@ -32,6 +31,7 @@ public class ModelRenderTest2 {
private float rotationAngle = 0f; private float rotationAngle = 0f;
private int testCase = 0; private int testCase = 0;
private Mesh2D squareMesh; private Mesh2D squareMesh;
public static void main(String[] args) { public static void main(String[] args) {
new ModelRenderTest2().run(); new ModelRenderTest2().run();
} }
@@ -104,7 +104,7 @@ public class ModelRenderTest2 {
ModelPart square = testModel.createPart("square"); ModelPart square = testModel.createPart("square");
square.setPosition(0, 0); // center of window square.setPosition(0, 0); // center of window
square.setPivot(0,0); square.setPivot(0, 0);
// Create 80x80 quad centered at origin // Create 80x80 quad centered at origin
squareMesh = Mesh2D.createQuad("square_mesh", 80, 80); squareMesh = Mesh2D.createQuad("square_mesh", 80, 80);
// Shift vertices so center is at (0,0) // 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.ModelPart;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D; import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.Texture; import com.chuangzhou.vivid2D.render.model.util.Texture;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@@ -15,6 +14,7 @@ import static org.lwjgl.opengl.GL11.*;
/** /**
* Texture Render Test Class - Debug Version * Texture Render Test Class - Debug Version
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class ModelRenderTextureTest { public class ModelRenderTextureTest {
@@ -143,10 +143,10 @@ public class ModelRenderTextureTest {
// 回退到手动创建顶点 // 回退到手动创建顶点
System.out.println("Using manual vertex creation"); System.out.println("Using manual vertex creation");
float[] vertices = { float[] vertices = {
-width/2, -height/2, // bottom left -width / 2, -height / 2, // bottom left
width/2, -height/2, // bottom right width / 2, -height / 2, // bottom right
width/2, height/2, // top right width / 2, height / 2, // top right
-width/2, height/2 // top left -width / 2, height / 2 // top left
}; };
float[] uvs = { float[] uvs = {
@@ -203,10 +203,10 @@ public class ModelRenderTextureTest {
} catch (Exception e) { } catch (Exception e) {
// 手动创建 // 手动创建
float[] vertices = { 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 = { float[] uvs = {
@@ -320,11 +320,16 @@ public class ModelRenderTextureTest {
private String getGLErrorString(int error) { private String getGLErrorString(int error) {
switch (error) { switch (error) {
case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; case GL_INVALID_ENUM:
case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; return "GL_INVALID_ENUM";
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; case GL_INVALID_VALUE:
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; return "GL_INVALID_VALUE";
default: return "Unknown Error (0x" + Integer.toHexString(error) + ")"; 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; 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.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart; 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.transform.WaveDeformer;
import com.chuangzhou.vivid2D.render.model.util.*; import com.chuangzhou.vivid2D.render.model.util.*;
import org.joml.Vector2f; import org.joml.Vector2f;
@@ -809,7 +809,6 @@ public class ModelTest {
} }
/** /**
* Utility method to print part hierarchy * 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.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D; 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.PhysicsSystem;
import com.chuangzhou.vivid2D.render.model.util.Texture;
import com.chuangzhou.vivid2D.render.systems.RenderSystem; import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import org.joml.Vector2f; import org.joml.Vector2f;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@@ -20,6 +20,7 @@ import java.util.List;
/** /**
* 物理系统使用实例 - 演示弹簧、重力和碰撞效果 * 物理系统使用实例 - 演示弹簧、重力和碰撞效果
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class ModelTest2 { public class ModelTest2 {
@@ -39,7 +40,7 @@ public class ModelTest2 {
private boolean springsEnabled = true; private boolean springsEnabled = true;
// 存储部件引用,用于清理 // 存储部件引用,用于清理
private List<ModelPart> currentParts = new ArrayList<>(); private final List<ModelPart> currentParts = new ArrayList<>();
// 所有测试基点(初始 xy = 0,0 // 所有测试基点(初始 xy = 0,0
private final Vector2f initialOrigin = new Vector2f(0, 0); private final Vector2f initialOrigin = new Vector2f(0, 0);
@@ -598,13 +599,20 @@ public class ModelTest2 {
*/ */
private String getTestCaseName(int testCase) { private String getTestCaseName(int testCase) {
switch (testCase) { switch (testCase) {
case 0: return "Spring Chain"; case 0:
case 1: return "Cloth Simulation"; return "Spring Chain";
case 2: return "Pendulum System"; case 1:
case 3: return "Soft Body"; return "Cloth Simulation";
case 4: return "Wind Test"; case 2:
case 5: return "Free Fall Test"; return "Pendulum System";
default: return "Unknown"; 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 的基础上增加简单动画(手臂、腿、头部摆动) * 在原 TestModelGLPanel 的基础上增加简单动画(手臂、腿、头部摆动)
*
* @author tzdwindows 7 * @author tzdwindows 7
*/ */
public class TestModelGLPanel { public class TestModelGLPanel {