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:
@@ -2,25 +2,30 @@ package com.chuangzhou.vivid2D.render;
|
||||
|
||||
import com.chuangzhou.vivid2D.render.model.Model2D;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.systems.Camera;
|
||||
import com.chuangzhou.vivid2D.render.systems.buffer.BufferBuilder;
|
||||
import com.chuangzhou.vivid2D.render.systems.buffer.Tesselator;
|
||||
import com.chuangzhou.vivid2D.render.model.util.LightSource;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem;
|
||||
import com.chuangzhou.vivid2D.render.systems.Camera;
|
||||
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
|
||||
import com.chuangzhou.vivid2D.render.systems.buffer.BufferBuilder;
|
||||
import com.chuangzhou.vivid2D.render.systems.buffer.Tesselator;
|
||||
import com.chuangzhou.vivid2D.render.systems.sources.CompleteShader;
|
||||
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
|
||||
import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement;
|
||||
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.*;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
@@ -65,6 +70,7 @@ public final class ModelRender {
|
||||
|
||||
/**
|
||||
* 渲染系统初始化状态标志,确保系统只初始化一次
|
||||
*
|
||||
* @see #initialize()
|
||||
* @see #isInitialized()
|
||||
*/
|
||||
@@ -73,6 +79,7 @@ public final class ModelRender {
|
||||
/**
|
||||
* 视口宽度(像素),定义渲染区域的大小
|
||||
* 默认值:800像素
|
||||
*
|
||||
* @see #setViewport(int, int)
|
||||
*/
|
||||
static int viewportWidth = 800;
|
||||
@@ -80,6 +87,7 @@ public final class ModelRender {
|
||||
/**
|
||||
* 视口高度(像素),定义渲染区域的大小
|
||||
* 默认值:600像素
|
||||
*
|
||||
* @see #setViewport(int, int)
|
||||
*/
|
||||
static int viewportHeight = 600;
|
||||
@@ -87,6 +95,7 @@ public final class ModelRender {
|
||||
/**
|
||||
* 清除颜色(RGBA),用于在每帧开始时清空颜色缓冲区
|
||||
* 默认值:黑色不透明 (0.0f, 0.0f, 0.0f, 1.0f)
|
||||
*
|
||||
* @see RenderSystem#clearColor(float, float, float, float)
|
||||
*/
|
||||
private static final Vector4f CLEAR_COLOR = new Vector4f(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
@@ -95,6 +104,7 @@ public final class ModelRender {
|
||||
* 深度测试启用标志,控制是否进行深度缓冲测试
|
||||
* 在2D渲染中通常禁用以提高性能
|
||||
* 默认值:false(禁用)
|
||||
*
|
||||
* @see RenderSystem#enableDepthTest()
|
||||
* @see RenderSystem#disableDepthTest()
|
||||
*/
|
||||
@@ -103,6 +113,7 @@ public final class ModelRender {
|
||||
/**
|
||||
* 混合功能启用标志,控制透明度和颜色混合
|
||||
* 默认值:true(启用)
|
||||
*
|
||||
* @see RenderSystem#enableBlend()
|
||||
* @see RenderSystem#disableBlend()
|
||||
*/
|
||||
@@ -119,6 +130,7 @@ public final class ModelRender {
|
||||
/**
|
||||
* 默认着色器程序,用于大多数模型的渲染
|
||||
* 包含基础的光照、纹理和变换功能
|
||||
*
|
||||
* @see #compileDefaultShader()
|
||||
*/
|
||||
private static ShaderProgram defaultProgram = null;
|
||||
@@ -127,6 +139,7 @@ public final class ModelRender {
|
||||
* 网格GPU资源缓存,管理已上传到GPU的网格数据
|
||||
* 键:Mesh2D对象
|
||||
* 值:对应的OpenGL资源(VAO、VBO、EBO)
|
||||
*
|
||||
* @see MeshGLResources
|
||||
*/
|
||||
private static final Map<Mesh2D, MeshGLResources> meshResources = new HashMap<>();
|
||||
@@ -141,6 +154,7 @@ public final class ModelRender {
|
||||
/**
|
||||
* 默认白色纹理ID,当模型没有指定纹理时使用
|
||||
* 这是一个1x1的纯白色纹理,确保模型有基本的颜色显示
|
||||
*
|
||||
* @see #createDefaultTexture()
|
||||
*/
|
||||
private static int defaultTextureId = 0;
|
||||
@@ -189,7 +203,7 @@ public final class ModelRender {
|
||||
*/
|
||||
private static final Camera camera = new Camera();
|
||||
|
||||
// ================== 字体管理 ==================
|
||||
// ================== 字体管理 ==================
|
||||
private static TextRenderer defaultTextRenderer = null;
|
||||
private static final int FONT_BITMAP_WIDTH = 512;
|
||||
private static final int FONT_BITMAP_HEIGHT = 512;
|
||||
@@ -290,9 +304,18 @@ public final class ModelRender {
|
||||
boolean initialized = false;
|
||||
|
||||
void dispose() {
|
||||
if (ebo != 0) { GL15.glDeleteBuffers(ebo); ebo = 0; }
|
||||
if (vbo != 0) { GL15.glDeleteBuffers(vbo); vbo = 0; }
|
||||
if (vao != 0) { GL30.glDeleteVertexArrays(vao); vao = 0; }
|
||||
if (ebo != 0) {
|
||||
GL15.glDeleteBuffers(ebo);
|
||||
ebo = 0;
|
||||
}
|
||||
if (vbo != 0) {
|
||||
GL15.glDeleteBuffers(vbo);
|
||||
vbo = 0;
|
||||
}
|
||||
if (vao != 0) {
|
||||
GL30.glDeleteVertexArrays(vao);
|
||||
vao = 0;
|
||||
}
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -722,12 +745,13 @@ public final class ModelRender {
|
||||
}
|
||||
}
|
||||
// 恢复原始颜色
|
||||
setUniformVec4Internal(defaultProgram, "uColor", new Vector4f(1,1,1,1));
|
||||
setUniformVec4Internal(defaultProgram, "uColor", new Vector4f(1, 1, 1, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制简洁的灯泡形状
|
||||
* @param position 灯泡位置
|
||||
*
|
||||
* @param position 灯泡位置
|
||||
* @param intensity 光源强度,用于控制灯泡大小
|
||||
*/
|
||||
private static void drawLightBulb(Vector2f position, float intensity) {
|
||||
@@ -868,16 +892,14 @@ public final class ModelRender {
|
||||
|
||||
RenderSystem.checkGLError("before_render_collider_" + enabledColliders);
|
||||
|
||||
if (collider instanceof PhysicsSystem.CircleCollider) {
|
||||
PhysicsSystem.CircleCollider c = (PhysicsSystem.CircleCollider) collider;
|
||||
if (collider instanceof PhysicsSystem.CircleCollider c) {
|
||||
if (c.getCenter() != null && c.getRadius() > 0) {
|
||||
drawCircleColliderWire(c.getCenter(), c.getRadius());
|
||||
enabledColliders++;
|
||||
} else {
|
||||
logger.warn("Invalid CircleCollider: center={}, radius={}", c.getCenter(), c.getRadius());
|
||||
}
|
||||
} else if (collider instanceof PhysicsSystem.RectangleCollider) {
|
||||
PhysicsSystem.RectangleCollider r = (PhysicsSystem.RectangleCollider) collider;
|
||||
} else if (collider instanceof PhysicsSystem.RectangleCollider r) {
|
||||
if (r.getCenter() != null && r.getWidth() > 0 && r.getHeight() > 0) {
|
||||
drawRectangleColliderWire(r.getCenter(), r.getWidth(), r.getHeight());
|
||||
enabledColliders++;
|
||||
@@ -977,17 +999,25 @@ public final class ModelRender {
|
||||
ModelPart.BlendMode bm = part.getBlendMode();
|
||||
if (bm != null) {
|
||||
switch (bm) {
|
||||
case ADDITIVE: blend = 1; break;
|
||||
case MULTIPLY: blend = 2; break;
|
||||
case SCREEN: blend = 3; break;
|
||||
case NORMAL: default: blend = 0;
|
||||
case ADDITIVE:
|
||||
blend = 1;
|
||||
break;
|
||||
case MULTIPLY:
|
||||
blend = 2;
|
||||
break;
|
||||
case SCREEN:
|
||||
blend = 3;
|
||||
break;
|
||||
case NORMAL:
|
||||
default:
|
||||
blend = 0;
|
||||
}
|
||||
} else {
|
||||
blend = 0;
|
||||
}
|
||||
setUniformIntInternal(sp, "uBlendMode", blend);
|
||||
// 这里保留为白色,若需要部件 tint 请替换为 part 的 color 属性
|
||||
setUniformVec4Internal(sp, "uColor", new Vector4f(1,1,1,1));
|
||||
setUniformVec4Internal(sp, "uColor", new Vector4f(1, 1, 1, 1));
|
||||
}
|
||||
|
||||
public static TextRenderer getTextRenderer() {
|
||||
@@ -1009,9 +1039,10 @@ public final class ModelRender {
|
||||
|
||||
/**
|
||||
* 渲染文字
|
||||
* @param text 文字内容
|
||||
* @param x 世界坐标 X
|
||||
* @param y 世界坐标 Y ,反转的
|
||||
*
|
||||
* @param text 文字内容
|
||||
* @param x 世界坐标 X
|
||||
* @param y 世界坐标 Y ,反转的
|
||||
* @param color RGBA 颜色
|
||||
*/
|
||||
public static void renderText(String text, float x, float y, Vector4f color) {
|
||||
@@ -1025,6 +1056,7 @@ public final class ModelRender {
|
||||
|
||||
/**
|
||||
* 获取默认摄像机与当前摄像机之间的偏移量
|
||||
*
|
||||
* @return Vector2f 偏移向量 (dx, dy)
|
||||
*/
|
||||
public static Vector2f getCameraOffset() {
|
||||
@@ -1033,15 +1065,16 @@ public final class ModelRender {
|
||||
float zoom = camera.getZoom();
|
||||
Vector2f pos = camera.getPosition();
|
||||
float tx = -1.0f - (2.0f * zoom * pos.x / width);
|
||||
float ty = 1.0f + (2.0f * zoom * pos.y / height);
|
||||
float ty = 1.0f + (2.0f * zoom * pos.y / height);
|
||||
float tx0 = -1.0f;
|
||||
float ty0 = 1.0f;
|
||||
float ty0 = 1.0f;
|
||||
float offsetX = tx - tx0;
|
||||
float offsetY = ty - ty0;
|
||||
offsetX = -offsetX * width / 2.0f / zoom;
|
||||
offsetY = offsetY * height / 2.0f / zoom;
|
||||
return new Vector2f(offsetX, offsetY);
|
||||
}
|
||||
|
||||
public static void setViewport(int width, int height) {
|
||||
viewportWidth = Math.max(1, width);
|
||||
viewportHeight = Math.max(1, height);
|
||||
@@ -1049,7 +1082,12 @@ public final class ModelRender {
|
||||
}
|
||||
|
||||
// ================== 辅助:外部获取状态 ==================
|
||||
public static boolean isInitialized() { return initialized; }
|
||||
public static int getLoadedMeshCount() { return meshResources.size(); }
|
||||
public static boolean isInitialized() {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
public static int getLoadedMeshCount() {
|
||||
return meshResources.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ import org.lwjgl.opengl.GL11;
|
||||
/**
|
||||
* 现代化选择框渲染器(性能优化版)
|
||||
* 主要优化点:
|
||||
* 1) 复用 Tesselator 单例 BufferBuilder,减少频繁的 GPU 资源创建/销毁
|
||||
* 2) 批量提交顶点:把同一 primitive(LINES / TRIANGLES / LINE_LOOP)与同一颜色的顶点尽量合并到一次 begin/end
|
||||
* 3) 手柄使用实心矩形(两三角形)批量绘制,保持美观且高效
|
||||
* 4) 增加轻微外发光(透明大边框)和阴影感以达到“现代”外观
|
||||
*
|
||||
* 1) 复用 Tesselator 单例 BufferBuilder,减少频繁的 GPU 资源创建/销毁
|
||||
* 2) 批量提交顶点:把同一 primitive(LINES / TRIANGLES / LINE_LOOP)与同一颜色的顶点尽量合并到一次 begin/end
|
||||
* 3) 手柄使用实心矩形(两三角形)批量绘制,保持美观且高效
|
||||
* 4) 增加轻微外发光(透明大边框)和阴影感以达到“现代”外观
|
||||
* <p>
|
||||
* 注意:本类依赖你工程中已有的 RenderSystem/Tesselator/BufferBuilder/BufferUploader 实现。
|
||||
*/
|
||||
public class MultiSelectionBoxRenderer {
|
||||
|
||||
@@ -11,9 +11,10 @@ import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
public interface ModelClickListener {
|
||||
/**
|
||||
* 当点击模型时触发
|
||||
* @param mesh 被点击的网格,如果点击在空白处则为 null
|
||||
* @param modelX 模型坐标系中的 X 坐标
|
||||
* @param modelY 模型坐标系中的 Y 坐标
|
||||
*
|
||||
* @param mesh 被点击的网格,如果点击在空白处则为 null
|
||||
* @param modelX 模型坐标系中的 X 坐标
|
||||
* @param modelY 模型坐标系中的 Y 坐标
|
||||
* @param screenX 屏幕坐标系中的 X 坐标
|
||||
* @param screenY 屏幕坐标系中的 Y 坐标
|
||||
*/
|
||||
@@ -21,22 +22,32 @@ public interface ModelClickListener {
|
||||
|
||||
/**
|
||||
* 当鼠标在模型上移动时触发
|
||||
* @param mesh 鼠标下方的网格,如果不在任何网格上则为 null
|
||||
* @param modelX 模型坐标系中的 X 坐标
|
||||
* @param modelY 模型坐标系中的 Y 坐标
|
||||
*
|
||||
* @param mesh 鼠标下方的网格,如果不在任何网格上则为 null
|
||||
* @param modelX 模型坐标系中的 X 坐标
|
||||
* @param modelY 模型坐标系中的 Y 坐标
|
||||
* @param screenX 屏幕坐标系中的 X 坐标
|
||||
* @param screenY 屏幕坐标系中的 Y 坐标
|
||||
*/
|
||||
default void onModelHover(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {}
|
||||
default void onLiquifyModeExited(){};
|
||||
default void onModelHover(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {
|
||||
}
|
||||
|
||||
default void onLiquifyModeEntered(Mesh2D targetMesh, ModelPart liquifyTargetPart){};
|
||||
default void onLiquifyModeExited() {
|
||||
}
|
||||
|
||||
default void onSecondaryVertexModeEntered(Mesh2D secondaryVertexTargetMesh){};
|
||||
default void onLiquifyModeEntered(Mesh2D targetMesh, ModelPart liquifyTargetPart) {
|
||||
}
|
||||
|
||||
default void onSecondaryVertexModeExited(){};
|
||||
default void onSecondaryVertexModeEntered(Mesh2D secondaryVertexTargetMesh) {
|
||||
}
|
||||
|
||||
default void onPuppetModeEntered(Mesh2D puppetTargetMesh){};
|
||||
default void onSecondaryVertexModeExited() {
|
||||
}
|
||||
|
||||
default void onPuppetModeEntered(Mesh2D puppetTargetMesh) {
|
||||
}
|
||||
|
||||
default void onPuppetModeExited() {
|
||||
}
|
||||
|
||||
default void onPuppetModeExited(){};
|
||||
}
|
||||
|
||||
@@ -16,11 +16,15 @@ import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.event.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
@@ -30,13 +34,13 @@ import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* ModelLayerPanel(完整实现)
|
||||
*
|
||||
* <p>
|
||||
* - 列表显示“从上到下”的图层(listModel[0] 为最上层)
|
||||
* - 在任何修改后都会把 model.parts 同步为列表的反序(保证渲染顺序与 UI 一致)
|
||||
* - 支持添加空层 / 从文件创建带贴图的层(在有 renderPanel 时在 GL 线程使用 Texture.createFromFile)
|
||||
* - 支持为选中部件绑定贴图、创建透明图层
|
||||
* - 支持拖拽重排、上下按钮移动,并在重排后正确恢复选中与不触发滑块事件
|
||||
*
|
||||
* <p>
|
||||
* 使用:
|
||||
* new ModelLayerPanel(model, optionalModelRenderPanel)
|
||||
*/
|
||||
@@ -242,7 +246,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
if (partMap != null) {
|
||||
partMap.put(uniqueName, part);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
part.setVisible(layerInfo.visible);
|
||||
|
||||
@@ -293,7 +298,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
// 强制尝试上传/初始化(若纹理对象需要)
|
||||
try {
|
||||
tryCallTextureUpload(texture);
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 标记模型需要更新
|
||||
model.markNeedsUpdate();
|
||||
@@ -306,10 +312,12 @@ public class ModelLayerPanel extends JPanel {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
reloadFromModel();
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
try {
|
||||
if (renderPanel != null) renderPanel.repaint();
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
System.err.println("创建纹理失败: " + uniqueName);
|
||||
@@ -505,7 +513,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(sel);
|
||||
if (v instanceof Float) op = (Float) v;
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
int val = Math.round(op * 100);
|
||||
|
||||
@@ -604,7 +613,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
Field f = sel.getClass().getDeclaredField("opacity");
|
||||
f.setAccessible(true);
|
||||
f.setFloat(sel, val / 100.0f);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (model != null) model.markNeedsUpdate();
|
||||
layerList.repaint();
|
||||
@@ -711,7 +721,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
w = Math.max(1, Integer.parseInt(sp[0].trim()));
|
||||
h = Math.max(1, Integer.parseInt(sp[1].trim()));
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
ModelPart part = model.createPart(name);
|
||||
@@ -753,20 +764,23 @@ public class ModelLayerPanel extends JPanel {
|
||||
File f = chooser.getSelectedFile();
|
||||
try {
|
||||
BufferedImage img = null;
|
||||
try { img = ImageIO.read(f); } catch (Exception ignored) {}
|
||||
try {
|
||||
img = ImageIO.read(f);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// 获取第一个 mesh
|
||||
Mesh2D targetMesh = null;
|
||||
try {
|
||||
Method getMeshes = sel.getClass().getMethod("getMeshes");
|
||||
Object list = getMeshes.invoke(sel);
|
||||
if (list instanceof List) {
|
||||
List<?> meshes = (List<?>) list;
|
||||
if (list instanceof List<?> meshes) {
|
||||
if (!meshes.isEmpty() && meshes.get(0) instanceof Mesh2D) {
|
||||
targetMesh = (Mesh2D) meshes.get(0);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
if (targetMesh == null) {
|
||||
if (img == null) {
|
||||
@@ -828,12 +842,12 @@ public class ModelLayerPanel extends JPanel {
|
||||
try {
|
||||
Method m = Mesh2D.class.getMethod("createQuad", String.class, float.class, float.class);
|
||||
Object o = m.invoke(null, meshName, w, h);
|
||||
if (o instanceof Mesh2D) {
|
||||
Mesh2D mesh = (Mesh2D) o;
|
||||
if (o instanceof Mesh2D mesh) {
|
||||
// 对基础四边形进行细分以增加顶点密度
|
||||
return subdivideMeshForLiquify(mesh, 3); // 3级细分
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建高密度网格(细分网格)
|
||||
@@ -914,8 +928,7 @@ public class ModelLayerPanel extends JPanel {
|
||||
if (cons != null) {
|
||||
cons.setAccessible(true);
|
||||
Object meshObj = cons.newInstance(name, vertices, uvs, indices);
|
||||
if (meshObj instanceof Mesh2D) {
|
||||
Mesh2D mesh = (Mesh2D) meshObj;
|
||||
if (meshObj instanceof Mesh2D mesh) {
|
||||
|
||||
// 设置合适的pivot(中心点)
|
||||
mesh.setPivot(0, 0);
|
||||
@@ -1074,11 +1087,12 @@ public class ModelLayerPanel extends JPanel {
|
||||
if (renderPanel == null) throw new IllegalStateException("需要 renderPanel 才能在 GL 上下文创建纹理");
|
||||
|
||||
try {
|
||||
return renderPanel.executeInGLContext((Callable<Texture>) () -> {
|
||||
return renderPanel.executeInGLContext(() -> {
|
||||
// 静态工厂尝试
|
||||
try {
|
||||
Method factory = findStaticMethod(Texture.class, "createFromBufferedImage", BufferedImage.class);
|
||||
if (factory == null) factory = findStaticMethod(Texture.class, "createFromImage", BufferedImage.class);
|
||||
if (factory == null)
|
||||
factory = findStaticMethod(Texture.class, "createFromImage", BufferedImage.class);
|
||||
if (factory != null) {
|
||||
Object texObj = factory.invoke(null, img);
|
||||
if (texObj instanceof Texture) {
|
||||
@@ -1086,7 +1100,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
return (Texture) texObj;
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 构造 ByteBuffer 并尝试构造器
|
||||
try {
|
||||
@@ -1123,15 +1138,20 @@ public class ModelLayerPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (formatEnum != null) {
|
||||
try {
|
||||
texObj = suit.newInstance(texName, w, h, formatEnum, buf);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (texObj == null) {
|
||||
try { texObj = suit.newInstance(texName, img.getWidth(), img.getHeight(), buf); } catch (Exception ignored) {}
|
||||
try {
|
||||
texObj = suit.newInstance(texName, img.getWidth(), img.getHeight(), buf);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (texObj instanceof Texture) {
|
||||
tryCallTextureUpload((Texture) texObj);
|
||||
@@ -1185,13 +1205,20 @@ public class ModelLayerPanel extends JPanel {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (formatEnum != null) {
|
||||
try { texObj = suit.newInstance(texName, w, h, formatEnum, buf); } catch (Throwable ignored) {}
|
||||
try {
|
||||
texObj = suit.newInstance(texName, w, h, formatEnum, buf);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (texObj == null) {
|
||||
try { texObj = suit.newInstance(texName, w, h, buf); } catch (Throwable ignored) {}
|
||||
try {
|
||||
texObj = suit.newInstance(texName, w, h, buf);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
if (texObj instanceof Texture) return (Texture) texObj;
|
||||
}
|
||||
@@ -1245,12 +1272,14 @@ public class ModelLayerPanel extends JPanel {
|
||||
try {
|
||||
Method m = cls.getMethod(name, param);
|
||||
if (Modifier.isStatic(m.getModifiers())) return m;
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
try {
|
||||
Method m = cls.getDeclaredMethod(name, param);
|
||||
m.setAccessible(true);
|
||||
if (Modifier.isStatic(m.getModifiers())) return m;
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1277,7 +1306,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
model.setRootPart(null);
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
model.markNeedsUpdate();
|
||||
reloadFromModel();
|
||||
} catch (Exception ex) {
|
||||
@@ -1449,9 +1479,7 @@ public class ModelLayerPanel extends JPanel {
|
||||
Field partsField = model.getClass().getDeclaredField("parts");
|
||||
partsField.setAccessible(true);
|
||||
Object old = partsField.get(model);
|
||||
if (old instanceof java.util.List) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
java.util.List rawList = (java.util.List) old;
|
||||
if (old instanceof @SuppressWarnings("rawtypes")List rawList) {
|
||||
rawList.clear();
|
||||
rawList.addAll(newParts);
|
||||
} else {
|
||||
@@ -1465,9 +1493,9 @@ public class ModelLayerPanel extends JPanel {
|
||||
// ============== 列表渲染/拖拽辅助 ==============
|
||||
|
||||
private class LayerCellRenderer extends JPanel implements ListCellRenderer<ModelPart> {
|
||||
private JCheckBox visibleBox = new JCheckBox();
|
||||
private JLabel nameLabel = new JLabel();
|
||||
private JLabel opacityLabel = new JLabel();
|
||||
private final JCheckBox visibleBox = new JCheckBox();
|
||||
private final JLabel nameLabel = new JLabel();
|
||||
private final JLabel opacityLabel = new JLabel();
|
||||
|
||||
LayerCellRenderer() {
|
||||
setLayout(new BorderLayout(6, 6));
|
||||
@@ -1543,7 +1571,9 @@ public class ModelLayerPanel extends JPanel {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceActions(JComponent c) { return MOVE; }
|
||||
public int getSourceActions(JComponent c) {
|
||||
return MOVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canImport(TransferSupport support) {
|
||||
@@ -1638,23 +1668,25 @@ public class ModelLayerPanel extends JPanel {
|
||||
try {
|
||||
Field fx = p.getClass().getDeclaredField("pivotX");
|
||||
Field fy = p.getClass().getDeclaredField("pivotY");
|
||||
fx.setAccessible(true); fy.setAccessible(true);
|
||||
fx.setAccessible(true);
|
||||
fy.setAccessible(true);
|
||||
px = ((Number) fx.get(p)).floatValue();
|
||||
py = ((Number) fy.get(p)).floatValue();
|
||||
} catch (Exception ignored2) {}
|
||||
} catch (Exception ignored2) {
|
||||
}
|
||||
}
|
||||
try {
|
||||
Method vm = p.getClass().getMethod("isVisible");
|
||||
visible = (Boolean) vm.invoke(p);
|
||||
} catch (Exception ignored) {}
|
||||
System.out.println(String.format("Part[%d] name=%s visible=%s pivot=(%.1f, %.1f)", i, name, visible, px, py));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
System.out.printf("Part[%d] name=%s visible=%s pivot=(%.1f, %.1f)%n", i, name, visible, px, py);
|
||||
|
||||
// meshes
|
||||
try {
|
||||
Method gmsh = p.getClass().getMethod("getMeshes");
|
||||
Object list = gmsh.invoke(p);
|
||||
if (list instanceof List) {
|
||||
List<?> meshes = (List<?>) list;
|
||||
if (list instanceof List<?> meshes) {
|
||||
System.out.println(" meshes count = " + meshes.size());
|
||||
for (int m = 0; m < meshes.size(); m++) {
|
||||
Object mesh = meshes.get(m);
|
||||
@@ -1667,7 +1699,8 @@ public class ModelLayerPanel extends JPanel {
|
||||
Field f = mesh.getClass().getDeclaredField("texture");
|
||||
f.setAccessible(true);
|
||||
tex = f.get(mesh);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
System.out.println(" mesh[" + m + "] texture = " + (tex == null ? "null" : tex.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
@@ -14,28 +14,27 @@ import com.chuangzhou.vivid2D.render.systems.RenderSystem;
|
||||
import com.chuangzhou.vivid2D.test.TestModelGLPanel;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector2f;
|
||||
import org.lwjgl.glfw.*;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.event.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
/**
|
||||
* vivid2D 模型的 Java 渲染面板
|
||||
@@ -43,11 +42,10 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
* <p>该类提供了 vivid2D 模型在 Java 环境下的图形渲染功能,
|
||||
* 包含基本的 2D 图形绘制、模型显示和交互操作。</p>
|
||||
*
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
* @version 1.1
|
||||
* @since 2025-10-13
|
||||
* @see TestModelGLPanel
|
||||
* @since 2025-10-13
|
||||
*/
|
||||
public class ModelRenderPanel extends JPanel {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModelRenderPanel.class);
|
||||
@@ -82,6 +80,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
private volatile float dragStartX, dragStartY;
|
||||
private volatile float partStartX, partStartY;
|
||||
private volatile boolean isDragging = false;
|
||||
|
||||
private enum DragMode {
|
||||
NONE, // 无拖拽
|
||||
MOVE, // 移动部件
|
||||
@@ -110,8 +109,8 @@ public class ModelRenderPanel extends JPanel {
|
||||
public static final float BORDER_THICKNESS = 6.0f;
|
||||
public static final float CORNER_SIZE = 12.0f;
|
||||
|
||||
private volatile float partInitialScaleX = 1.0f;
|
||||
private volatile float partInitialScaleY = 1.0f;
|
||||
private final float partInitialScaleX = 1.0f;
|
||||
private final float partInitialScaleY = 1.0f;
|
||||
|
||||
private volatile float displayScale = 1.0f; // 当前可视缩放(用于检测阈值/角点等)
|
||||
private volatile float targetScale = 1.0f; // 目标缩放(鼠标滚轮/程序改变时设置)
|
||||
@@ -121,23 +120,23 @@ public class ModelRenderPanel extends JPanel {
|
||||
private static final float ZOOM_MAX = 8.0f;
|
||||
private volatile boolean shiftDuringDrag = false;
|
||||
private volatile float rotationStartAngle = 0.0f;
|
||||
private volatile float partInitialRotation = 0.0f;
|
||||
private volatile Vector2f rotationCenter = new Vector2f();
|
||||
private final float partInitialRotation = 0.0f;
|
||||
private final Vector2f rotationCenter = new Vector2f();
|
||||
private static final float ROTATION_HANDLE_DISTANCE = 30.0f;
|
||||
private OperationHistoryManager historyManager;
|
||||
|
||||
// 新增:操作历史管理器
|
||||
private OperationHistoryGlobal operationHistory;
|
||||
private final OperationHistoryGlobal operationHistory;
|
||||
|
||||
// 新增:拖拽操作的状态记录字段
|
||||
private Map<ModelPart, Vector2f> dragStartPositions = new HashMap<>();
|
||||
private Map<ModelPart, Vector2f> dragStartScales = new HashMap<>();
|
||||
private Map<ModelPart, Float> dragStartRotations = new HashMap<>();
|
||||
private Map<ModelPart, Vector2f> dragStartPivots = new HashMap<>();
|
||||
private final Map<ModelPart, Vector2f> dragStartPositions = new HashMap<>();
|
||||
private final Map<ModelPart, Vector2f> dragStartScales = new HashMap<>();
|
||||
private final Map<ModelPart, Float> dragStartRotations = new HashMap<>();
|
||||
private final Map<ModelPart, Vector2f> dragStartPivots = new HashMap<>();
|
||||
|
||||
// 新增:鼠标手势相关字段
|
||||
private volatile Cursor currentCursor = Cursor.getDefaultCursor();
|
||||
private volatile DragMode hoverDragMode = DragMode.NONE;
|
||||
private final DragMode hoverDragMode = DragMode.NONE;
|
||||
private volatile boolean isOverSelection = false;
|
||||
|
||||
// ================== 摄像机控制相关字段 ==================
|
||||
@@ -1036,7 +1035,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
* 退出液化模式
|
||||
*/
|
||||
private void exitLiquifyMode() {
|
||||
executeInGLContext(()->{
|
||||
executeInGLContext(() -> {
|
||||
liquifyMode = false;
|
||||
if (liquifyTargetPart != null) {
|
||||
liquifyTargetPart.setStartLiquefy(false);
|
||||
@@ -1112,7 +1111,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
|
||||
// 绘制圆圈
|
||||
int center = size / 2;
|
||||
int radius = (int)(liquifyBrushSize * 0.1f); // 根据画笔大小缩放光标
|
||||
int radius = (int) (liquifyBrushSize * 0.1f); // 根据画笔大小缩放光标
|
||||
|
||||
// 外圈
|
||||
g2d.setColor(Color.RED);
|
||||
@@ -3342,7 +3341,10 @@ public class ModelRenderPanel extends JPanel {
|
||||
// 确保缓冲区大小匹配(可能在 resize 后需要重建)
|
||||
if (pixelBuffer == null || pixelInts == null || pixelInts.length != pixelCount) {
|
||||
if (pixelBuffer != null) {
|
||||
try { MemoryUtil.memFree(pixelBuffer); } catch (Throwable ignored) {}
|
||||
try {
|
||||
MemoryUtil.memFree(pixelBuffer);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
pixelBuffer = MemoryUtil.memAlloc(pixelCount * 4);
|
||||
pixelBuffer.order(ByteOrder.nativeOrder());
|
||||
@@ -3431,6 +3433,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
|
||||
/**
|
||||
* 在 GL 上下文线程上异步执行任务
|
||||
*
|
||||
* @param task 要在 GL 上下文线程中执行的任务
|
||||
* @return CompletableFuture 用于获取任务执行结果
|
||||
*/
|
||||
@@ -3464,6 +3467,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
|
||||
/**
|
||||
* 在 GL 上下文线程上异步执行任务并返回结果
|
||||
*
|
||||
* @param task 要在 GL 上下文线程中执行的有返回值的任务
|
||||
* @return CompletableFuture 用于获取任务执行结果
|
||||
*/
|
||||
@@ -3495,6 +3499,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
|
||||
/**
|
||||
* 同步在 GL 上下文线程上执行任务(会阻塞当前线程直到任务完成)
|
||||
*
|
||||
* @param task 要在 GL 上下文线程中执行的任务
|
||||
* @throws Exception 如果任务执行出错
|
||||
*/
|
||||
@@ -3509,6 +3514,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
|
||||
/**
|
||||
* 同步在 GL 上下文线程上执行任务并返回结果(会阻塞当前线程直到任务完成)
|
||||
*
|
||||
* @param task 要在 GL 上下文线程中执行的有返回值的任务
|
||||
* @return 任务执行结果
|
||||
* @throws Exception 如果任务执行出错或超时
|
||||
@@ -3541,7 +3547,7 @@ public class ModelRenderPanel extends JPanel {
|
||||
|
||||
/**
|
||||
* 重新设置面板大小
|
||||
*
|
||||
* <p>
|
||||
* 说明:当 Swing 面板被放大时,需要同时调整离屏 GLFW 窗口像素大小、GL 视口以及重分配像素读取缓冲,
|
||||
* 否则将把较小分辨率的图像拉伸到更大面板上导致模糊。
|
||||
*/
|
||||
@@ -3637,7 +3643,8 @@ public class ModelRenderPanel extends JPanel {
|
||||
if (windowId != 0) {
|
||||
try {
|
||||
GLFW.glfwDestroyWindow(windowId);
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
windowId = 0;
|
||||
}
|
||||
|
||||
@@ -3654,7 +3661,8 @@ public class ModelRenderPanel extends JPanel {
|
||||
// 终止 GLFW(注意:如果应用中还有其他 GLFW 窗口,这里会影响它们)
|
||||
try {
|
||||
GLFW.glfwTerminate();
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
logger.info("OpenGL 资源已清理");
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
package com.chuangzhou.vivid2D.render.awt;
|
||||
|
||||
import com.chuangzhou.vivid2D.render.awt.util.OperationHistoryGlobal;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelEvent;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import org.joml.Vector2f;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class TransformPanel extends JPanel implements ModelEvent {
|
||||
private ModelRenderPanel renderPanel;
|
||||
private List<ModelPart> selectedParts = new ArrayList<>();
|
||||
private final ModelRenderPanel renderPanel;
|
||||
private final List<ModelPart> selectedParts = new ArrayList<>();
|
||||
private boolean isMultiSelection = false;
|
||||
|
||||
// 位置控制
|
||||
@@ -47,7 +47,7 @@ public class TransformPanel extends JPanel implements ModelEvent {
|
||||
private boolean updatingUI = false; // 防止UI更新时触发事件
|
||||
private javax.swing.Timer transformTimer; // 用于延迟处理变换输入
|
||||
|
||||
private OperationHistoryGlobal operationHistory;
|
||||
private final OperationHistoryGlobal operationHistory;
|
||||
|
||||
public TransformPanel(ModelRenderPanel renderPanel) {
|
||||
this.renderPanel = renderPanel;
|
||||
|
||||
@@ -704,16 +704,14 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void handleBatchTransformRecord(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Object[] oldValues = (Object[]) params[1];
|
||||
Object[] newValues = (Object[]) params[2];
|
||||
}
|
||||
}
|
||||
|
||||
private void executeBatchTransform(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Object[] newValues = (Object[]) params[2];
|
||||
|
||||
// 应用新的变换值
|
||||
@@ -734,8 +732,7 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void undoBatchTransform(Object... params) {
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart part) {
|
||||
Object[] oldValues = (Object[]) params[1];
|
||||
|
||||
// 恢复旧的变换值
|
||||
@@ -814,8 +811,7 @@ public class OperationHistoryGlobal {
|
||||
// 从参数中获取当前位置(参数索引从2开始)
|
||||
int paramIndex = 2;
|
||||
for (ModelPart part : parts) {
|
||||
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f) {
|
||||
Vector2f targetPosition = (Vector2f) params[paramIndex];
|
||||
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f targetPosition) {
|
||||
part.setPosition(targetPosition.x, targetPosition.y);
|
||||
paramIndex++;
|
||||
}
|
||||
@@ -830,8 +826,7 @@ public class OperationHistoryGlobal {
|
||||
// 从参数中获取当前缩放(参数索引从2开始)
|
||||
int paramIndex = 2;
|
||||
for (ModelPart part : parts) {
|
||||
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f) {
|
||||
Vector2f targetScale = (Vector2f) params[paramIndex];
|
||||
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f targetScale) {
|
||||
part.setScale(targetScale.x, targetScale.y);
|
||||
paramIndex++;
|
||||
}
|
||||
@@ -862,8 +857,7 @@ public class OperationHistoryGlobal {
|
||||
// 从参数中获取当前中心点(参数索引从2开始)
|
||||
int paramIndex = 2;
|
||||
for (ModelPart part : parts) {
|
||||
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f) {
|
||||
Vector2f targetPivot = (Vector2f) params[paramIndex];
|
||||
if (paramIndex < params.length && params[paramIndex] instanceof Vector2f targetPivot) {
|
||||
part.setPivot(targetPivot.x, targetPivot.y);
|
||||
paramIndex++;
|
||||
}
|
||||
@@ -883,7 +877,7 @@ public class OperationHistoryGlobal {
|
||||
part.setPosition(startPosition.x, startPosition.y);
|
||||
}
|
||||
}
|
||||
// System.out.println("撤回拖拽结束操作");
|
||||
// System.out.println("撤回拖拽结束操作");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,8 +930,7 @@ public class OperationHistoryGlobal {
|
||||
// ============ 具体操作处理方法 ============
|
||||
|
||||
private void handlePositionRecord(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Vector2f oldPosition = (Vector2f) params[1];
|
||||
Vector2f newPosition = (Vector2f) params[2];
|
||||
|
||||
@@ -948,24 +941,21 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void executePositionChange(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Vector2f newPosition = (Vector2f) params[2];
|
||||
part.setPosition(newPosition.x, newPosition.y);
|
||||
}
|
||||
}
|
||||
|
||||
private void undoPositionChange(Object... params) {
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart part) {
|
||||
Vector2f oldPosition = (Vector2f) params[1];
|
||||
part.setPosition(oldPosition.x, oldPosition.y);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRotationRecord(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
float oldRotation = (Float) params[1];
|
||||
float newRotation = (Float) params[2];
|
||||
|
||||
@@ -975,24 +965,21 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void executeRotationChange(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
float newRotation = (Float) params[2];
|
||||
part.setRotation(newRotation);
|
||||
}
|
||||
}
|
||||
|
||||
private void undoRotationChange(Object... params) {
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart part) {
|
||||
float oldRotation = (Float) params[1];
|
||||
part.setRotation(oldRotation);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleScaleRecord(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Vector2f oldScale = (Vector2f) params[1];
|
||||
Vector2f newScale = (Vector2f) params[2];
|
||||
|
||||
@@ -1002,24 +989,21 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void executeScaleChange(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Vector2f newScale = (Vector2f) params[2];
|
||||
part.setScale(newScale.x, newScale.y);
|
||||
}
|
||||
}
|
||||
|
||||
private void undoScaleChange(Object... params) {
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart part) {
|
||||
Vector2f oldScale = (Vector2f) params[1];
|
||||
part.setScale(oldScale.x, oldScale.y);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOpacityRecord(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
float oldOpacity = (Float) params[1];
|
||||
float newOpacity = (Float) params[2];
|
||||
|
||||
@@ -1029,24 +1013,21 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void executeOpacityChange(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
float newOpacity = (Float) params[2];
|
||||
part.setOpacity(newOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
private void undoOpacityChange(Object... params) {
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart part) {
|
||||
float oldOpacity = (Float) params[1];
|
||||
part.setOpacity(oldOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleVisibleRecord(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
boolean oldVisible = (Boolean) params[1];
|
||||
boolean newVisible = (Boolean) params[2];
|
||||
|
||||
@@ -1056,24 +1037,21 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void executeVisibleChange(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
boolean newVisible = (Boolean) params[2];
|
||||
part.setVisible(newVisible);
|
||||
}
|
||||
}
|
||||
|
||||
private void undoVisibleChange(Object... params) {
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart part) {
|
||||
boolean oldVisible = (Boolean) params[1];
|
||||
part.setVisible(oldVisible);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePivotRecord(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Vector2f oldPivot = (Vector2f) params[1];
|
||||
Vector2f newPivot = (Vector2f) params[2];
|
||||
|
||||
@@ -1083,16 +1061,14 @@ public class OperationHistoryGlobal {
|
||||
}
|
||||
|
||||
private void executePivotChange(Object... params) {
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 3 && params[0] instanceof ModelPart part) {
|
||||
Vector2f newPivot = (Vector2f) params[2];
|
||||
part.setPivot(newPivot.x, newPivot.y);
|
||||
}
|
||||
}
|
||||
|
||||
private void undoPivotChange(Object... params) {
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart) {
|
||||
ModelPart part = (ModelPart) params[0];
|
||||
if (params.length >= 2 && params[0] instanceof ModelPart part) {
|
||||
Vector2f oldPivot = (Vector2f) params[1];
|
||||
part.setPivot(oldPivot.x, oldPivot.y);
|
||||
}
|
||||
@@ -1459,10 +1435,21 @@ public class OperationHistoryGlobal {
|
||||
this.canRedo = canRedo;
|
||||
}
|
||||
|
||||
public int getRegisteredOperationCount() { return registeredOperationCount; }
|
||||
public int getHistorySize() { return historySize; }
|
||||
public boolean canUndo() { return canUndo; }
|
||||
public boolean canRedo() { return canRedo; }
|
||||
public int getRegisteredOperationCount() {
|
||||
return registeredOperationCount;
|
||||
}
|
||||
|
||||
public int getHistorySize() {
|
||||
return historySize;
|
||||
}
|
||||
|
||||
public boolean canUndo() {
|
||||
return canUndo;
|
||||
}
|
||||
|
||||
public boolean canRedo() {
|
||||
return canRedo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.chuangzhou.vivid2D.render.awt.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作记录管理器
|
||||
* 负责管理操作的撤回和重做
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class OperationHistoryManager {
|
||||
private static OperationHistoryManager instance = new OperationHistoryManager();
|
||||
private static final OperationHistoryManager instance = new OperationHistoryManager();
|
||||
// 操作记录栈
|
||||
private final LinkedList<OperationRecord> undoStack;
|
||||
private final LinkedList<OperationRecord> redoStack;
|
||||
@@ -35,6 +38,7 @@ public class OperationHistoryManager {
|
||||
|
||||
/**
|
||||
* 获取操作记录管理器实例
|
||||
*
|
||||
* @return 操作记录管理器实例
|
||||
*/
|
||||
public static OperationHistoryManager getInstance() {
|
||||
@@ -43,8 +47,9 @@ public class OperationHistoryManager {
|
||||
|
||||
/**
|
||||
* 注册操作记录器
|
||||
*
|
||||
* @param operationType 操作类型标识
|
||||
* @param recorder 操作记录器
|
||||
* @param recorder 操作记录器
|
||||
*/
|
||||
public void registerRecorder(String operationType, OperationRecorder recorder) {
|
||||
recorderMap.put(operationType, recorder);
|
||||
@@ -52,8 +57,9 @@ public class OperationHistoryManager {
|
||||
|
||||
/**
|
||||
* 记录操作
|
||||
*
|
||||
* @param operationType 操作类型
|
||||
* @param params 操作参数
|
||||
* @param params 操作参数
|
||||
*/
|
||||
public void recordOperation(String operationType, Object... params) {
|
||||
if (!enabled) return;
|
||||
@@ -203,9 +209,20 @@ public class OperationHistoryManager {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String getOperationType() { return operationType; }
|
||||
public Object[] getParams() { return params; }
|
||||
public String getDescription() { return description; }
|
||||
public long getTimestamp() { return timestamp; }
|
||||
public String getOperationType() {
|
||||
return operationType;
|
||||
}
|
||||
|
||||
public Object[] getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ public interface OperationListener {
|
||||
|
||||
/**
|
||||
* 操作事件回调
|
||||
*
|
||||
* @param operationType 操作类型
|
||||
* @param action 动作类型(record, execute, undo, redo, clear)
|
||||
* @param params 操作参数
|
||||
* @param action 动作类型(record, execute, undo, redo, clear)
|
||||
* @param params 操作参数
|
||||
*/
|
||||
void onOperationEvent(String operationType, String action, Object... params);
|
||||
}
|
||||
@@ -3,24 +3,28 @@ package com.chuangzhou.vivid2D.render.awt.util;
|
||||
/**
|
||||
* 操作记录接口
|
||||
* 用于注册需要支持撤回/重做的操作
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public interface OperationRecorder {
|
||||
|
||||
/**
|
||||
* 执行操作(用于重做)
|
||||
*
|
||||
* @param params 操作参数
|
||||
*/
|
||||
void execute(Object... params);
|
||||
|
||||
/**
|
||||
* 撤销操作
|
||||
*
|
||||
* @param params 操作参数
|
||||
*/
|
||||
void undo(Object... params);
|
||||
|
||||
/**
|
||||
* 获取操作描述(用于UI显示)
|
||||
*
|
||||
* @return 操作描述
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
@@ -109,7 +109,7 @@ public class PSD_Structure_Dumper {
|
||||
}
|
||||
System.out.println(" --- 额外数据块遍历结束 ---");
|
||||
// 确保指针移动到下一个图层记录的开始
|
||||
if(fis.getChannel().position() != extraDataEndPos) {
|
||||
if (fis.getChannel().position() != extraDataEndPos) {
|
||||
long diff = extraDataEndPos - fis.getChannel().position();
|
||||
System.out.printf("!!! 指针与预期不符,强制跳过 %d 字节以对齐下一个图层%n", diff);
|
||||
skipFully(dis, diff);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.chuangzhou.vivid2D.render.model;
|
||||
|
||||
public class AnimationParameter {
|
||||
private String id;
|
||||
private final String id;
|
||||
private float value;
|
||||
private float defaultValue;
|
||||
private float minValue;
|
||||
private float maxValue;
|
||||
private final float defaultValue;
|
||||
private final float minValue;
|
||||
private final float maxValue;
|
||||
private boolean changed = false;
|
||||
|
||||
public AnimationParameter(String id, float min, float max, float defaultValue) {
|
||||
@@ -24,18 +24,33 @@ public class AnimationParameter {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasChanged() { return changed; }
|
||||
public void markClean() { this.changed = false; }
|
||||
public boolean hasChanged() {
|
||||
return changed;
|
||||
}
|
||||
|
||||
public float getValue() { return value; }
|
||||
public void markClean() {
|
||||
this.changed = false;
|
||||
}
|
||||
|
||||
public String getId() { return id; }
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public float getMinValue() { return minValue; }
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public float getMaxValue() { return maxValue; }
|
||||
public float getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
public float getDefaultValue() { return defaultValue; }
|
||||
public float getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public float getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.value = defaultValue;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.chuangzhou.vivid2D.render.model;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import com.chuangzhou.vivid2D.render.model.data.ModelData;
|
||||
import com.chuangzhou.vivid2D.render.model.data.ModelMetadata;
|
||||
import com.chuangzhou.vivid2D.render.model.util.*;
|
||||
import org.joml.Matrix3f;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -94,6 +94,7 @@ public class Model2D {
|
||||
}
|
||||
|
||||
// ==================== 姿态管理 ====================
|
||||
|
||||
/**
|
||||
* 添加或更新姿态
|
||||
*/
|
||||
@@ -559,21 +560,42 @@ public class Model2D {
|
||||
}
|
||||
|
||||
// ==================== Getter/Setter ====================
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public UUID getUuid() { return uuid; }
|
||||
public void setUuid(UUID uuid) { this.uuid = uuid; }
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public ModelMetadata getMetadata() { return metadata; }
|
||||
public void setMetadata(ModelMetadata metadata) { this.metadata = metadata; }
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public ModelPart getRootPart() { return rootPart; }
|
||||
public void setRootPart(ModelPart rootPart) { this.rootPart = rootPart; }
|
||||
public void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public ModelMetadata getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
public void setMetadata(ModelMetadata metadata) {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public ModelPart getRootPart() {
|
||||
return rootPart;
|
||||
}
|
||||
|
||||
public void setRootPart(ModelPart rootPart) {
|
||||
this.rootPart = rootPart;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<Mesh2D> getMeshes() { return Collections.unmodifiableList(meshes); }
|
||||
public List<Mesh2D> getMeshes() {
|
||||
return Collections.unmodifiableList(meshes);
|
||||
}
|
||||
|
||||
public Map<String, AnimationParameter> getParameters() {
|
||||
return Collections.unmodifiableMap(parameters);
|
||||
@@ -583,17 +605,35 @@ public class Model2D {
|
||||
return Collections.unmodifiableList(animationLayers);
|
||||
}
|
||||
|
||||
public PhysicsSystem getPhysics() { return physics; }
|
||||
public PhysicsSystem getPhysics() {
|
||||
return physics;
|
||||
}
|
||||
|
||||
public ModelPose getCurrentPose() {
|
||||
return new ModelPose(currentPose);
|
||||
}
|
||||
public float getBlendProgress() { return blendProgress; }
|
||||
public boolean isBlending() { return blendProgress < 1.0f; }
|
||||
|
||||
public float getBlendProgress() {
|
||||
return blendProgress;
|
||||
}
|
||||
|
||||
public boolean isBlending() {
|
||||
return blendProgress < 1.0f;
|
||||
}
|
||||
|
||||
public Map<String, ModelPose> getPoses() {
|
||||
return Collections.unmodifiableMap(poses);
|
||||
}
|
||||
public BoundingBox getBounds() { return bounds; }
|
||||
|
||||
public String getVersion() { return version; }
|
||||
public void setVersion(String version) { this.version = version; }
|
||||
public BoundingBox getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,16 @@ package com.chuangzhou.vivid2D.render.model;
|
||||
import com.chuangzhou.vivid2D.render.awt.util.OperationHistoryGlobal;
|
||||
import com.chuangzhou.vivid2D.render.model.util.BoundingBox;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Deformer;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.chuangzhou.vivid2D.render.model.util.PuppetPin;
|
||||
import com.chuangzhou.vivid2D.render.systems.Matrix3fUtils;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector2f;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 2D模型部件,支持层级变换和变形器
|
||||
@@ -54,7 +54,7 @@ public class ModelPart {
|
||||
|
||||
private final List<ModelEvent> events = new ArrayList<>();
|
||||
private boolean inMultiSelectionOperation = false;
|
||||
private boolean startLiquefy =false;
|
||||
private boolean startLiquefy = false;
|
||||
|
||||
// ====== 液化模式枚举 ======
|
||||
public enum LiquifyMode {
|
||||
@@ -112,7 +112,7 @@ public class ModelPart {
|
||||
|
||||
private void triggerEvent(String eventName) {
|
||||
for (ModelEvent event : events) {
|
||||
event.trigger(eventName,this);
|
||||
event.trigger(eventName, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,20 +527,20 @@ public class ModelPart {
|
||||
if (stroke == null || stroke.points == null) return;
|
||||
LiquifyMode mode = stroke.mode != null ? stroke.mode : LiquifyMode.PUSH;
|
||||
for (LiquifyPoint p : stroke.points) {
|
||||
applyLiquify(new Vector2f(p.x, p.y), stroke.radius, stroke.strength, mode, stroke.iterations,true);
|
||||
applyLiquify(new Vector2f(p.x, p.y), stroke.radius, stroke.strength, mode, stroke.iterations, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对当前部件下所有网格应用液化笔效果(类似 Photoshop 的液化工具)。
|
||||
* 请在使用前注册在使用addLiquifyStroke方法注册LiquifyStroke
|
||||
*
|
||||
* <p>
|
||||
* 注意:
|
||||
* - brushCenter 使用世界坐标(与 ModelPart 的世界坐标体系一致)。
|
||||
* - radius 为画笔半径(像素),strength 为强度(建议范围 0.0 - 1.0,数值越大效果越强)。
|
||||
* - mode 选择液化操作类型。
|
||||
* - iterations 为迭代次数(>0),可用来让效果更平滑,默认 1 次即可。
|
||||
*
|
||||
* <p>
|
||||
* 该方法会直接修改 mesh 的顶点并更新其边界(mesh.updateBounds)。
|
||||
*/
|
||||
public void applyLiquify(Vector2f brushCenter, float radius, float strength, LiquifyMode mode, int iterations, boolean createVertices) {
|
||||
@@ -928,7 +928,7 @@ public class ModelPart {
|
||||
float brushArea = brushAreaWidth * brushAreaHeight;
|
||||
|
||||
// 根据画笔大小和网格复杂度计算顶点密度
|
||||
int targetVertexCount = Math.max(4, Math.min(20, (int)(brushArea / (localRadius * localRadius * 0.5f))));
|
||||
int targetVertexCount = Math.max(4, Math.min(20, (int) (brushArea / (localRadius * localRadius * 0.5f))));
|
||||
|
||||
// 在画笔区域内均匀添加顶点
|
||||
boolean addedVertices = addUniformVerticesInArea(mesh, newVertices, newUVs, newIndices,
|
||||
@@ -1899,7 +1899,6 @@ public class ModelPart {
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setScale(float uniformScale) {
|
||||
// 记录旧的世界变换,用于计算 pivot 的相对位置
|
||||
Matrix3f oldWorldTransform = new Matrix3f(this.worldTransform);
|
||||
@@ -1992,7 +1991,8 @@ public class ModelPart {
|
||||
Vector2f origPivot = mesh.getOriginalPivot();
|
||||
Vector2f worldPivot = Matrix3fUtils.transformPoint(this.worldTransform, origPivot);
|
||||
mesh.setPivot(worldPivot.x, worldPivot.y);
|
||||
} catch (Exception ignored) { }
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// ==================== 新增:初始化木偶控制点的位置 ====================
|
||||
initializePuppetPinsPosition(mesh);
|
||||
@@ -2079,7 +2079,7 @@ public class ModelPart {
|
||||
// 由于 Mesh2D 的 originalPivot 已经存储了其在 ModelPart 局部坐标系中的相对位置,
|
||||
// 我们可以直接将 ModelPart 的新 pivot 赋值给 Mesh2D 的 originalPivot
|
||||
// 然后再通过变换更新 Mesh2D 的实际 pivot
|
||||
if (!mesh.setOriginalPivot(new Vector2f(x, y))){
|
||||
if (!mesh.setOriginalPivot(new Vector2f(x, y))) {
|
||||
return false;
|
||||
}
|
||||
// Mesh2D 的实际 pivot 应该根据 ModelPart 的世界变换来计算
|
||||
@@ -2289,8 +2289,13 @@ public class ModelPart {
|
||||
return opacity;
|
||||
}
|
||||
|
||||
public float getScaleX() { return scaleX; }
|
||||
public float getScaleY() { return scaleY; }
|
||||
public float getScaleX() {
|
||||
return scaleX;
|
||||
}
|
||||
|
||||
public float getScaleY() {
|
||||
return scaleY;
|
||||
}
|
||||
|
||||
public void setOpacity(float opacity) {
|
||||
this.opacity = Math.max(0.0f, Math.min(1.0f, opacity));
|
||||
@@ -2306,7 +2311,8 @@ public class ModelPart {
|
||||
public float y;
|
||||
public float pressure = 1.0f;
|
||||
|
||||
public LiquifyPoint() {}
|
||||
public LiquifyPoint() {
|
||||
}
|
||||
|
||||
public LiquifyPoint(float x, float y) {
|
||||
this.x = x;
|
||||
@@ -2319,9 +2325,17 @@ public class ModelPart {
|
||||
this.pressure = pressure;
|
||||
}
|
||||
|
||||
public float getX() { return x; }
|
||||
public float getY() { return y; }
|
||||
public float getPressure() { return pressure; }
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public float getPressure() {
|
||||
return pressure;
|
||||
}
|
||||
}
|
||||
|
||||
// ====== 液化笔划数据结构(包含点序列与笔划参数),提供 getter 以便反射读取 ======
|
||||
@@ -2332,7 +2346,8 @@ public class ModelPart {
|
||||
public int iterations = 1;
|
||||
public List<LiquifyPoint> points = new ArrayList<>();
|
||||
|
||||
public LiquifyStroke() {}
|
||||
public LiquifyStroke() {
|
||||
}
|
||||
|
||||
public LiquifyStroke(LiquifyMode mode, float radius, float strength, int iterations) {
|
||||
this.mode = mode;
|
||||
@@ -2341,11 +2356,25 @@ public class ModelPart {
|
||||
this.iterations = iterations;
|
||||
}
|
||||
|
||||
public String getMode() { return mode.name(); } // PartData 反射时读取字符串也可
|
||||
public float getRadius() { return radius; }
|
||||
public float getStrength() { return strength; }
|
||||
public int getIterations() { return iterations; }
|
||||
public List<LiquifyPoint> getPoints() { return points; }
|
||||
public String getMode() {
|
||||
return mode.name();
|
||||
} // PartData 反射时读取字符串也可
|
||||
|
||||
public float getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
public float getStrength() {
|
||||
return strength;
|
||||
}
|
||||
|
||||
public int getIterations() {
|
||||
return iterations;
|
||||
}
|
||||
|
||||
public List<LiquifyPoint> getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
public void addPoint(float x, float y, float pressure) {
|
||||
this.points.add(new LiquifyPoint(x, y, pressure));
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 动画层数据
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class AnimationLayerData implements Serializable {
|
||||
@@ -151,7 +152,8 @@ public class AnimationLayerData implements Serializable {
|
||||
public float value;
|
||||
public AnimationLayer.InterpolationType interpolation;
|
||||
|
||||
public KeyframeData() {}
|
||||
public KeyframeData() {
|
||||
}
|
||||
|
||||
public KeyframeData(AnimationLayer.Keyframe keyframe) {
|
||||
this.time = keyframe.getTime();
|
||||
|
||||
@@ -286,7 +286,8 @@ public class MeshData implements Serializable {
|
||||
public Vector2f uv;
|
||||
public boolean selected;
|
||||
|
||||
public SecondaryVertexData() {}
|
||||
public SecondaryVertexData() {
|
||||
}
|
||||
|
||||
public SecondaryVertexData(SecondaryVertex vertex) {
|
||||
this.id = vertex.getId();
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.util.zip.GZIPOutputStream;
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ModelData implements Serializable {
|
||||
private static Logger logger = LoggerFactory.getLogger(ModelData.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(ModelData.class);
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// ==================== 模型元数据 ====================
|
||||
@@ -39,11 +39,11 @@ public class ModelData implements Serializable {
|
||||
private List<ParameterData> parameters;
|
||||
private List<AnimationData> animations;
|
||||
private List<AnimationLayerData> animationLayers;
|
||||
private List<ParticleData> physicsParticles;
|
||||
private List<SpringData> physicsSprings;
|
||||
private List<ColliderData> physicsColliders;
|
||||
private List<ConstraintData> physicsConstraints;
|
||||
private List<LightSourceData> lights;
|
||||
private final List<ParticleData> physicsParticles;
|
||||
private final List<SpringData> physicsSprings;
|
||||
private final List<ColliderData> physicsColliders;
|
||||
private final List<ConstraintData> physicsConstraints;
|
||||
private final List<LightSourceData> lights;
|
||||
private List<PoseData> poses;
|
||||
private String currentPoseName; // 当前应用的姿态名称
|
||||
|
||||
@@ -163,8 +163,7 @@ public class ModelData implements Serializable {
|
||||
|
||||
// 约束(仅序列化常见两类)
|
||||
for (PhysicsSystem.PhysicsConstraint c : phys.getConstraints()) {
|
||||
if (c instanceof PhysicsSystem.PositionConstraint) {
|
||||
PhysicsSystem.PositionConstraint pc = (PhysicsSystem.PositionConstraint) c;
|
||||
if (c instanceof PhysicsSystem.PositionConstraint pc) {
|
||||
ConstraintData cd = new ConstraintData();
|
||||
cd.type = "position";
|
||||
cd.particleId = pc.getParticle().getId();
|
||||
@@ -174,8 +173,7 @@ public class ModelData implements Serializable {
|
||||
cd.strength = pc.getStrength();
|
||||
cd.enabled = pc.isEnabled();
|
||||
physicsConstraints.add(cd);
|
||||
} else if (c instanceof PhysicsSystem.DistanceConstraint) {
|
||||
PhysicsSystem.DistanceConstraint dc = (PhysicsSystem.DistanceConstraint) c;
|
||||
} else if (c instanceof PhysicsSystem.DistanceConstraint dc) {
|
||||
ConstraintData cd = new ConstraintData();
|
||||
cd.type = "distance";
|
||||
cd.particleId = dc.getParticle().getId();
|
||||
@@ -193,14 +191,12 @@ public class ModelData implements Serializable {
|
||||
ColliderData cd = new ColliderData();
|
||||
cd.id = collider.getId();
|
||||
cd.enabled = collider.isEnabled();
|
||||
if (collider instanceof PhysicsSystem.CircleCollider) {
|
||||
PhysicsSystem.CircleCollider cc = (PhysicsSystem.CircleCollider) collider;
|
||||
if (collider instanceof PhysicsSystem.CircleCollider cc) {
|
||||
cd.type = "circle";
|
||||
cd.centerX = cc.getCenter().x;
|
||||
cd.centerY = cc.getCenter().y;
|
||||
cd.radius = cc.getRadius();
|
||||
} else if (collider instanceof PhysicsSystem.RectangleCollider) {
|
||||
PhysicsSystem.RectangleCollider rc = (PhysicsSystem.RectangleCollider) collider;
|
||||
} else if (collider instanceof PhysicsSystem.RectangleCollider rc) {
|
||||
cd.type = "rect";
|
||||
cd.centerX = rc.getCenter().x;
|
||||
cd.centerY = rc.getCenter().y;
|
||||
@@ -538,7 +534,6 @@ public class ModelData implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Map<String, Texture> deserializeTextures() {
|
||||
Map<String, Texture> textureMap = new HashMap<>();
|
||||
|
||||
@@ -859,7 +854,6 @@ public class ModelData implements Serializable {
|
||||
// ==================== 内部数据类 ====================
|
||||
|
||||
|
||||
|
||||
// ---------- 物理数据的序列化类 ----------
|
||||
public static class ParticleData implements Serializable {
|
||||
public String id;
|
||||
@@ -956,7 +950,6 @@ public class ModelData implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 参数数据
|
||||
*/
|
||||
@@ -969,7 +962,8 @@ public class ModelData implements Serializable {
|
||||
public float minValue;
|
||||
public float maxValue;
|
||||
|
||||
public ParameterData() {}
|
||||
public ParameterData() {
|
||||
}
|
||||
|
||||
public ParameterData(AnimationParameter param) {
|
||||
this.id = param.getId();
|
||||
@@ -1042,60 +1036,146 @@ public class ModelData implements Serializable {
|
||||
|
||||
// ==================== Getter/Setter ====================
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getVersion() { return version; }
|
||||
public void setVersion(String version) { this.version = version; }
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public UUID getUuid() { return uuid; }
|
||||
public void setUuid(UUID uuid) { this.uuid = uuid; }
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getAuthor() { return author; }
|
||||
public void setAuthor(String author) { this.author = author; }
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public long getCreationTime() { return creationTime; }
|
||||
public void setCreationTime(long creationTime) { this.creationTime = creationTime; }
|
||||
public void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public long getLastModifiedTime() { return lastModifiedTime; }
|
||||
public void setLastModifiedTime(long lastModifiedTime) { this.lastModifiedTime = lastModifiedTime; }
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public List<PartData> getParts() { return parts; }
|
||||
public void setParts(List<PartData> parts) { this.parts = parts; }
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public List<MeshData> getMeshes() { return meshes; }
|
||||
public void setMeshes(List<MeshData> meshes) { this.meshes = meshes; }
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public List<TextureData> getTextures() { return textures; }
|
||||
public void setTextures(List<TextureData> textures) { this.textures = textures; }
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public List<ParameterData> getParameters() { return parameters; }
|
||||
public void setParameters(List<ParameterData> parameters) { this.parameters = parameters; }
|
||||
public long getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
public List<AnimationData> getAnimations() { return animations; }
|
||||
public void setAnimations(List<AnimationData> animations) { this.animations = animations; }
|
||||
public void setCreationTime(long creationTime) {
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
|
||||
public Vector2f getPivotPoint() { return pivotPoint; }
|
||||
public void setPivotPoint(Vector2f pivotPoint) { this.pivotPoint = pivotPoint; }
|
||||
public long getLastModifiedTime() {
|
||||
return lastModifiedTime;
|
||||
}
|
||||
|
||||
public float getUnitsPerMeter() { return unitsPerMeter; }
|
||||
public void setUnitsPerMeter(float unitsPerMeter) { this.unitsPerMeter = unitsPerMeter; }
|
||||
public void setLastModifiedTime(long lastModifiedTime) {
|
||||
this.lastModifiedTime = lastModifiedTime;
|
||||
}
|
||||
|
||||
public Map<String, String> getUserData() { return userData; }
|
||||
public void setUserData(Map<String, String> userData) { this.userData = userData; }
|
||||
public List<PartData> getParts() {
|
||||
return parts;
|
||||
}
|
||||
|
||||
public void setParts(List<PartData> parts) {
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
public List<MeshData> getMeshes() {
|
||||
return meshes;
|
||||
}
|
||||
|
||||
public void setMeshes(List<MeshData> meshes) {
|
||||
this.meshes = meshes;
|
||||
}
|
||||
|
||||
public List<TextureData> getTextures() {
|
||||
return textures;
|
||||
}
|
||||
|
||||
public void setTextures(List<TextureData> textures) {
|
||||
this.textures = textures;
|
||||
}
|
||||
|
||||
public List<ParameterData> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public void setParameters(List<ParameterData> parameters) {
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public List<AnimationData> getAnimations() {
|
||||
return animations;
|
||||
}
|
||||
|
||||
public void setAnimations(List<AnimationData> animations) {
|
||||
this.animations = animations;
|
||||
}
|
||||
|
||||
public Vector2f getPivotPoint() {
|
||||
return pivotPoint;
|
||||
}
|
||||
|
||||
public void setPivotPoint(Vector2f pivotPoint) {
|
||||
this.pivotPoint = pivotPoint;
|
||||
}
|
||||
|
||||
public float getUnitsPerMeter() {
|
||||
return unitsPerMeter;
|
||||
}
|
||||
|
||||
public void setUnitsPerMeter(float unitsPerMeter) {
|
||||
this.unitsPerMeter = unitsPerMeter;
|
||||
}
|
||||
|
||||
public Map<String, String> getUserData() {
|
||||
return userData;
|
||||
}
|
||||
|
||||
public void setUserData(Map<String, String> userData) {
|
||||
this.userData = userData;
|
||||
}
|
||||
|
||||
public List<AnimationLayerData> getAnimationLayers() {
|
||||
return animationLayers;
|
||||
}
|
||||
|
||||
public List<AnimationLayerData> getAnimationLayers() { return animationLayers; }
|
||||
public void setAnimationLayers(List<AnimationLayerData> animationLayers) {
|
||||
this.animationLayers = animationLayers;
|
||||
}
|
||||
|
||||
public List<PoseData> getPoses() { return poses; }
|
||||
public void setPoses(List<PoseData> poses) { this.poses = poses; }
|
||||
public List<PoseData> getPoses() {
|
||||
return poses;
|
||||
}
|
||||
|
||||
public void setPoses(List<PoseData> poses) {
|
||||
this.poses = poses;
|
||||
}
|
||||
|
||||
public String getCurrentPoseName() {
|
||||
return currentPoseName;
|
||||
}
|
||||
|
||||
public String getCurrentPoseName() { return currentPoseName; }
|
||||
public void setCurrentPoseName(String currentPoseName) {
|
||||
this.currentPoseName = currentPoseName;
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ public class ModelMetadata implements Serializable, Cloneable {
|
||||
// 粗略估算:顶点数据 + 纹理数据 + 其他开销
|
||||
long vertexDataSize = (long) vertexCount * 8 * 2; // 每个顶点8字节(float x,y),2份(原始+变形)
|
||||
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元数据
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 部件姿态数据序列化类
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class PartPoseData implements Serializable {
|
||||
@@ -19,7 +20,8 @@ public class PartPoseData implements Serializable {
|
||||
public boolean visible;
|
||||
public float colorR, colorG, colorB;
|
||||
|
||||
public PartPoseData() {}
|
||||
public PartPoseData() {
|
||||
}
|
||||
|
||||
public PartPoseData(String partName, ModelPose.PartPose partPose) {
|
||||
this.partName = partName;
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 姿态数据序列化类
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class PoseData implements Serializable {
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.chuangzhou.vivid2D.render.model.util.Deformer;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import org.joml.Vector2f;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -98,11 +97,23 @@ public class RotationDeformer extends Deformer {
|
||||
}
|
||||
|
||||
// Getter/Setter
|
||||
public float getBaseAngle() { return baseAngle; }
|
||||
public void setBaseAngle(float baseAngle) { this.baseAngle = baseAngle; }
|
||||
public float getBaseAngle() {
|
||||
return baseAngle;
|
||||
}
|
||||
|
||||
public float getAngleRange() { return angleRange; }
|
||||
public void setAngleRange(float angleRange) { this.angleRange = angleRange; }
|
||||
public void setBaseAngle(float baseAngle) {
|
||||
this.baseAngle = baseAngle;
|
||||
}
|
||||
|
||||
public float getCurrentAngle() { return currentAngle; }
|
||||
public float getAngleRange() {
|
||||
return angleRange;
|
||||
}
|
||||
|
||||
public void setAngleRange(float angleRange) {
|
||||
this.angleRange = angleRange;
|
||||
}
|
||||
|
||||
public float getCurrentAngle() {
|
||||
return currentAngle;
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,10 @@ public class VertexDeformer extends Deformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶点变形数据内部类
|
||||
*/
|
||||
private record VertexDeformation(float originalX, float originalY, float targetX, float targetY) { }
|
||||
* 顶点变形数据内部类
|
||||
*/
|
||||
private record VertexDeformation(float originalX, float originalY, float targetX, float targetY) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加顶点变形目标
|
||||
@@ -212,17 +213,20 @@ public class VertexDeformer extends Deformer {
|
||||
try {
|
||||
String enabledKey = map.get(name + ".enabled");
|
||||
if (enabledKey != null) this.enabled = Boolean.parseBoolean(enabledKey);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
String weightKey = map.get(name + ".weight");
|
||||
if (weightKey != null) this.weight = Float.parseFloat(weightKey);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
String curKey = map.get(name + ".currentValue");
|
||||
if (curKey != null) this.currentValue = Float.parseFloat(curKey);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
// 清空已有数据
|
||||
this.vertexDeformations.clear();
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.chuangzhou.vivid2D.render.model.util.Deformer;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import org.joml.Vector2f;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -247,7 +246,8 @@ public class WaveDeformer extends Deformer {
|
||||
public float timeMultiplier = 1.0f;
|
||||
public float weight = 1.0f;
|
||||
|
||||
public WaveConfig() {}
|
||||
public WaveConfig() {
|
||||
}
|
||||
|
||||
public WaveConfig(float amplitude, float frequency, float phase,
|
||||
float waveAngle, float timeMultiplier, float weight) {
|
||||
@@ -287,24 +287,56 @@ public class WaveDeformer extends Deformer {
|
||||
timeMultiplier = Float.parseFloat(map.get("timeMultiplier"));
|
||||
}
|
||||
|
||||
public float getTimeMultiplier() { return timeMultiplier; }
|
||||
public void setTimeMultiplier(float timeMultiplier) { this.timeMultiplier = timeMultiplier; }
|
||||
public float getTimeMultiplier() {
|
||||
return timeMultiplier;
|
||||
}
|
||||
|
||||
public ControlMode getControlMode() { return controlMode; }
|
||||
public void setTimeMultiplier(float timeMultiplier) {
|
||||
this.timeMultiplier = timeMultiplier;
|
||||
}
|
||||
|
||||
public ControlMode getControlMode() {
|
||||
return controlMode;
|
||||
}
|
||||
|
||||
// Getter/Setter
|
||||
public float getAmplitude() { return amplitude; }
|
||||
public void setAmplitude(float amplitude) { this.amplitude = amplitude; }
|
||||
public float getAmplitude() {
|
||||
return amplitude;
|
||||
}
|
||||
|
||||
public float getFrequency() { return frequency; }
|
||||
public void setFrequency(float frequency) { this.frequency = frequency; }
|
||||
public void setAmplitude(float amplitude) {
|
||||
this.amplitude = amplitude;
|
||||
}
|
||||
|
||||
public float getPhase() { return phase; }
|
||||
public void setPhase(float phase) { this.phase = phase; }
|
||||
public float getFrequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
public float getWaveAngle() { return waveAngle; }
|
||||
public void setWaveAngle(float waveAngle) { this.waveAngle = waveAngle; }
|
||||
public void setFrequency(float frequency) {
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public float getCurrentTime() { return currentTime; }
|
||||
public void setCurrentTime(float currentTime) { this.currentTime = currentTime; }
|
||||
public float getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
public void setPhase(float phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
public float getWaveAngle() {
|
||||
return waveAngle;
|
||||
}
|
||||
|
||||
public void setWaveAngle(float waveAngle) {
|
||||
this.waveAngle = waveAngle;
|
||||
}
|
||||
|
||||
public float getCurrentTime() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
public void setCurrentTime(float currentTime) {
|
||||
this.currentTime = currentTime;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class AnimationLayer {
|
||||
private float currentTime;
|
||||
private boolean playing;
|
||||
private boolean paused;
|
||||
private Map<String, Float> parameterOverrides;
|
||||
private final Map<String, Float> parameterOverrides;
|
||||
|
||||
// ==================== 事件系统 ====================
|
||||
private final List<AnimationEventListener> eventListeners;
|
||||
@@ -468,22 +468,45 @@ public class AnimationLayer {
|
||||
|
||||
// ==================== Getter/Setter ====================
|
||||
|
||||
public String getName() { return name; }
|
||||
public UUID getUuid() { return uuid; }
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public float getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public float getWeight() { return weight; }
|
||||
public void setWeight(float weight) {
|
||||
this.weight = Math.max(0.0f, Math.min(1.0f, weight));
|
||||
}
|
||||
|
||||
public boolean isEnabled() { return enabled; }
|
||||
public void setEnabled(boolean enabled) { this.enabled = enabled; }
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public BlendMode getBlendMode() { return blendMode; }
|
||||
public void setBlendMode(BlendMode blendMode) { this.blendMode = blendMode; }
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getPriority() { return priority; }
|
||||
public void setPriority(int priority) { this.priority = priority; }
|
||||
public BlendMode getBlendMode() {
|
||||
return blendMode;
|
||||
}
|
||||
|
||||
public void setBlendMode(BlendMode blendMode) {
|
||||
this.blendMode = blendMode;
|
||||
}
|
||||
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public Map<String, AnimationTrack> getTracks() {
|
||||
return Collections.unmodifiableMap(tracks);
|
||||
@@ -493,20 +516,37 @@ public class AnimationLayer {
|
||||
return Collections.unmodifiableList(clips);
|
||||
}
|
||||
|
||||
public AnimationClip getCurrentClip() { return currentClip; }
|
||||
public AnimationClip getCurrentClip() {
|
||||
return currentClip;
|
||||
}
|
||||
|
||||
public float getPlaybackSpeed() {
|
||||
return playbackSpeed;
|
||||
}
|
||||
|
||||
public float getPlaybackSpeed() { return playbackSpeed; }
|
||||
public void setPlaybackSpeed(float playbackSpeed) {
|
||||
this.playbackSpeed = Math.max(0.0f, playbackSpeed);
|
||||
}
|
||||
|
||||
public boolean isLooping() { return looping; }
|
||||
public void setLooping(boolean looping) { this.looping = looping; }
|
||||
public boolean isLooping() {
|
||||
return looping;
|
||||
}
|
||||
|
||||
public float getCurrentTime() { return currentTime; }
|
||||
public void setLooping(boolean looping) {
|
||||
this.looping = looping;
|
||||
}
|
||||
|
||||
public boolean isPlaying() { return playing; }
|
||||
public boolean isPaused() { return paused; }
|
||||
public float getCurrentTime() {
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
public boolean isPlaying() {
|
||||
return playing;
|
||||
}
|
||||
|
||||
public boolean isPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
public Map<String, Float> getParameterOverrides() {
|
||||
return Collections.unmodifiableMap(parameterOverrides);
|
||||
@@ -616,12 +656,29 @@ public class AnimationLayer {
|
||||
}
|
||||
|
||||
// Getter/Setter
|
||||
public String getParameterId() { return parameterId; }
|
||||
public List<Keyframe> getKeyframes() { return Collections.unmodifiableList(keyframes); }
|
||||
public boolean isEnabled() { return enabled; }
|
||||
public void setEnabled(boolean enabled) { this.enabled = enabled; }
|
||||
public InterpolationType getInterpolation() { return interpolation; }
|
||||
public void setInterpolation(InterpolationType interpolation) { this.interpolation = interpolation; }
|
||||
public String getParameterId() {
|
||||
return parameterId;
|
||||
}
|
||||
|
||||
public List<Keyframe> getKeyframes() {
|
||||
return Collections.unmodifiableList(keyframes);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public InterpolationType getInterpolation() {
|
||||
return interpolation;
|
||||
}
|
||||
|
||||
public void setInterpolation(InterpolationType interpolation) {
|
||||
this.interpolation = interpolation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,9 +700,17 @@ public class AnimationLayer {
|
||||
}
|
||||
|
||||
// Getter
|
||||
public float getTime() { return time; }
|
||||
public float getValue() { return value; }
|
||||
public InterpolationType getInterpolation() { return interpolation; }
|
||||
public float getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public InterpolationType getInterpolation() {
|
||||
return interpolation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -687,9 +752,17 @@ public class AnimationLayer {
|
||||
}
|
||||
|
||||
// Getter
|
||||
public String getName() { return name; }
|
||||
public float getTime() { return time; }
|
||||
public boolean isTriggered() { return triggered; }
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public float getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public boolean isTriggered() {
|
||||
return triggered;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -697,11 +770,17 @@ public class AnimationLayer {
|
||||
*/
|
||||
public interface AnimationEventListener {
|
||||
void onAnimationStarted(AnimationLayer layer, AnimationClip clip);
|
||||
|
||||
void onAnimationStopped(AnimationLayer layer, AnimationClip clip);
|
||||
|
||||
void onAnimationPaused(AnimationLayer layer, AnimationClip clip);
|
||||
|
||||
void onAnimationResumed(AnimationLayer layer, AnimationClip clip);
|
||||
|
||||
void onAnimationCompleted(AnimationLayer layer, AnimationClip clip);
|
||||
|
||||
void onAnimationLooped(AnimationLayer layer, AnimationClip clip);
|
||||
|
||||
void onEventTriggered(AnimationLayer layer, AnimationEvent event);
|
||||
}
|
||||
|
||||
@@ -709,13 +788,33 @@ public class AnimationLayer {
|
||||
* 简单的动画事件监听器适配器
|
||||
*/
|
||||
public static abstract class AnimationEventAdapter implements AnimationEventListener {
|
||||
@Override public void onAnimationStarted(AnimationLayer layer, AnimationClip clip) {}
|
||||
@Override public void onAnimationStopped(AnimationLayer layer, AnimationClip clip) {}
|
||||
@Override public void onAnimationPaused(AnimationLayer layer, AnimationClip clip) {}
|
||||
@Override public void onAnimationResumed(AnimationLayer layer, AnimationClip clip) {}
|
||||
@Override public void onAnimationCompleted(AnimationLayer layer, AnimationClip clip) {}
|
||||
@Override public void onAnimationLooped(AnimationLayer layer, AnimationClip clip) {}
|
||||
@Override public void onEventTriggered(AnimationLayer layer, AnimationEvent event) {}
|
||||
@Override
|
||||
public void onAnimationStarted(AnimationLayer layer, AnimationClip clip) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStopped(AnimationLayer layer, AnimationClip clip) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationPaused(AnimationLayer layer, AnimationClip clip) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationResumed(AnimationLayer layer, AnimationClip clip) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCompleted(AnimationLayer layer, AnimationClip clip) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationLooped(AnimationLayer layer, AnimationClip clip) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEventTriggered(AnimationLayer layer, AnimationEvent event) {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Object 方法 ====================
|
||||
|
||||
@@ -229,12 +229,13 @@ public abstract class Deformer {
|
||||
* 变形范围控制
|
||||
*/
|
||||
public static class DeformationRange {
|
||||
private Vector2f center = new Vector2f(0, 0);
|
||||
private final Vector2f center = new Vector2f(0, 0);
|
||||
private float radius = 100.0f;
|
||||
private float innerRadius = 0.0f;
|
||||
private float falloff = 2.0f;
|
||||
|
||||
public DeformationRange() {}
|
||||
public DeformationRange() {
|
||||
}
|
||||
|
||||
public DeformationRange(Vector2f center, float radius) {
|
||||
this.center.set(center);
|
||||
@@ -263,17 +264,40 @@ public abstract class Deformer {
|
||||
}
|
||||
|
||||
// Getter/Setter
|
||||
public Vector2f getCenter() { return new Vector2f(center); }
|
||||
public void setCenter(Vector2f center) { this.center.set(center); }
|
||||
public void setCenter(float x, float y) { this.center.set(x, y); }
|
||||
public Vector2f getCenter() {
|
||||
return new Vector2f(center);
|
||||
}
|
||||
|
||||
public float getRadius() { return radius; }
|
||||
public void setRadius(float radius) { this.radius = radius; }
|
||||
public void setCenter(Vector2f center) {
|
||||
this.center.set(center);
|
||||
}
|
||||
|
||||
public float getInnerRadius() { return innerRadius; }
|
||||
public void setInnerRadius(float innerRadius) { this.innerRadius = innerRadius; }
|
||||
public void setCenter(float x, float y) {
|
||||
this.center.set(x, y);
|
||||
}
|
||||
|
||||
public float getFalloff() { return falloff; }
|
||||
public void setFalloff(float falloff) { this.falloff = falloff; }
|
||||
public float getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
public void setRadius(float radius) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
public float getInnerRadius() {
|
||||
return innerRadius;
|
||||
}
|
||||
|
||||
public void setInnerRadius(float innerRadius) {
|
||||
this.innerRadius = innerRadius;
|
||||
}
|
||||
|
||||
public float getFalloff() {
|
||||
return falloff;
|
||||
}
|
||||
|
||||
public void setFalloff(float falloff) {
|
||||
this.falloff = falloff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,13 @@ import java.awt.*;
|
||||
|
||||
/**
|
||||
* 光源系统
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class LightSource {
|
||||
private Vector2f position;
|
||||
private Vector3f color;
|
||||
private float intensity;
|
||||
private final Vector2f position;
|
||||
private final Vector3f color;
|
||||
private final float intensity;
|
||||
private boolean enabled = true;
|
||||
private boolean isAmbient = false; // 是否为环境光
|
||||
|
||||
@@ -73,31 +74,73 @@ public class LightSource {
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
public Vector2f getPosition() { return position; }
|
||||
public Vector3f getColor() { return color; }
|
||||
public float getIntensity() { return intensity; }
|
||||
public boolean isEnabled() { return enabled; }
|
||||
public void setEnabled(boolean enabled) { this.enabled = enabled; }
|
||||
public Vector2f getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public Vector3f getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public float getIntensity() {
|
||||
return intensity;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
// 判断是否为环境光
|
||||
public boolean isAmbient() { return isAmbient; }
|
||||
public void setAmbient(boolean ambient) { this.isAmbient = ambient; }
|
||||
public boolean isAmbient() {
|
||||
return isAmbient;
|
||||
}
|
||||
|
||||
public void setAmbient(boolean ambient) {
|
||||
this.isAmbient = ambient;
|
||||
}
|
||||
|
||||
// ---- 辉光相关的 getter / setter ----
|
||||
public boolean isGlow() { return isGlow; }
|
||||
public void setGlow(boolean glow) { this.isGlow = glow; }
|
||||
public boolean isGlow() {
|
||||
return isGlow;
|
||||
}
|
||||
|
||||
public void setGlow(boolean glow) {
|
||||
this.isGlow = glow;
|
||||
}
|
||||
|
||||
public Vector2f getGlowDirection() {
|
||||
return glowDirection;
|
||||
}
|
||||
|
||||
public Vector2f getGlowDirection() { return glowDirection; }
|
||||
public void setGlowDirection(Vector2f glowDirection) {
|
||||
this.glowDirection = glowDirection != null ? glowDirection : new Vector2f(0f, 0f);
|
||||
}
|
||||
|
||||
public float getGlowIntensity() { return glowIntensity; }
|
||||
public void setGlowIntensity(float glowIntensity) { this.glowIntensity = glowIntensity; }
|
||||
public float getGlowIntensity() {
|
||||
return glowIntensity;
|
||||
}
|
||||
|
||||
public float getGlowRadius() { return glowRadius; }
|
||||
public void setGlowRadius(float glowRadius) { this.glowRadius = glowRadius; }
|
||||
public void setGlowIntensity(float glowIntensity) {
|
||||
this.glowIntensity = glowIntensity;
|
||||
}
|
||||
|
||||
public float getGlowAmount() { return glowAmount; }
|
||||
public void setGlowAmount(float glowAmount) { this.glowAmount = glowAmount; }
|
||||
public float getGlowRadius() {
|
||||
return glowRadius;
|
||||
}
|
||||
|
||||
public void setGlowRadius(float glowRadius) {
|
||||
this.glowRadius = glowRadius;
|
||||
}
|
||||
|
||||
public float getGlowAmount() {
|
||||
return glowAmount;
|
||||
}
|
||||
|
||||
public void setGlowAmount(float glowAmount) {
|
||||
this.glowAmount = glowAmount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ public class ModelPose {
|
||||
* 单个部件的姿态数据
|
||||
*/
|
||||
public static class PartPose {
|
||||
private Vector2f position;
|
||||
private final Vector2f position;
|
||||
private float rotation;
|
||||
private Vector2f scale;
|
||||
private final Vector2f scale;
|
||||
private float opacity;
|
||||
private boolean visible;
|
||||
private Vector3f color; // RGB颜色乘数
|
||||
private final Vector3f color; // RGB颜色乘数
|
||||
|
||||
public PartPose() {
|
||||
this(new Vector2f(0, 0), 0.0f, new Vector2f(1, 1), 1.0f, true, new Vector3f(1, 1, 1));
|
||||
@@ -93,23 +93,53 @@ public class ModelPose {
|
||||
|
||||
// ================== Getter和Setter ==================
|
||||
|
||||
public Vector2f getPosition() { return new Vector2f(position); }
|
||||
public void setPosition(Vector2f position) { this.position.set(position); }
|
||||
public Vector2f getPosition() {
|
||||
return new Vector2f(position);
|
||||
}
|
||||
|
||||
public float getRotation() { return rotation; }
|
||||
public void setRotation(float rotation) { this.rotation = rotation; }
|
||||
public void setPosition(Vector2f position) {
|
||||
this.position.set(position);
|
||||
}
|
||||
|
||||
public Vector2f getScale() { return new Vector2f(scale); }
|
||||
public void setScale(Vector2f scale) { this.scale.set(scale); }
|
||||
public float getRotation() {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public float getOpacity() { return opacity; }
|
||||
public void setOpacity(float opacity) { this.opacity = opacity; }
|
||||
public void setRotation(float rotation) {
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
public boolean isVisible() { return visible; }
|
||||
public void setVisible(boolean visible) { this.visible = visible; }
|
||||
public Vector2f getScale() {
|
||||
return new Vector2f(scale);
|
||||
}
|
||||
|
||||
public Vector3f getColor() { return new Vector3f(color); }
|
||||
public void setColor(Vector3f color) { this.color.set(color); }
|
||||
public void setScale(Vector2f scale) {
|
||||
this.scale.set(scale);
|
||||
}
|
||||
|
||||
public float getOpacity() {
|
||||
return opacity;
|
||||
}
|
||||
|
||||
public void setOpacity(float opacity) {
|
||||
this.opacity = opacity;
|
||||
}
|
||||
|
||||
public boolean isVisible() {
|
||||
return visible;
|
||||
}
|
||||
|
||||
public void setVisible(boolean visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
public Vector3f getColor() {
|
||||
return new Vector3f(color);
|
||||
}
|
||||
|
||||
public void setColor(Vector3f color) {
|
||||
this.color.set(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
@@ -357,14 +387,29 @@ public class ModelPose {
|
||||
|
||||
// ================== Getter和Setter ==================
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public float getBlendTime() { return blendTime; }
|
||||
public void setBlendTime(float blendTime) { this.blendTime = Math.max(0, blendTime); }
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isDefaultPose() { return isDefaultPose; }
|
||||
public void setDefaultPose(boolean defaultPose) { isDefaultPose = defaultPose; }
|
||||
public float getBlendTime() {
|
||||
return blendTime;
|
||||
}
|
||||
|
||||
public void setBlendTime(float blendTime) {
|
||||
this.blendTime = Math.max(0, blendTime);
|
||||
}
|
||||
|
||||
public boolean isDefaultPose() {
|
||||
return isDefaultPose;
|
||||
}
|
||||
|
||||
public void setDefaultPose(boolean defaultPose) {
|
||||
isDefaultPose = defaultPose;
|
||||
}
|
||||
|
||||
// ================== 工具方法 ==================
|
||||
|
||||
|
||||
@@ -12,15 +12,15 @@ public class PuppetPin {
|
||||
private static int NEXT_ID = 0;
|
||||
|
||||
private int id;
|
||||
private Vector2f position;
|
||||
private Vector2f originalPosition;
|
||||
private Vector2f uv;
|
||||
private final Vector2f position;
|
||||
private final Vector2f originalPosition;
|
||||
private final Vector2f uv;
|
||||
private float influenceRadius = 100.0f;
|
||||
private boolean selected = false;
|
||||
private String name;
|
||||
|
||||
// 权重贴图(顶点索引 -> 权重值)
|
||||
private Map<Integer, Float> weightMap = new HashMap<>();
|
||||
private final Map<Integer, Float> weightMap = new HashMap<>();
|
||||
|
||||
public PuppetPin(float x, float y, float u, float v) {
|
||||
this.id = NEXT_ID++;
|
||||
@@ -97,18 +97,53 @@ public class PuppetPin {
|
||||
// ==================== 原有方法 ====================
|
||||
|
||||
// Getters and Setters
|
||||
public Vector2f getPosition() { return new Vector2f(position); }
|
||||
public void setPosition(float x, float y) { this.position.set(x, y); }
|
||||
public void setPosition(Vector2f pos) { this.position.set(pos); }
|
||||
public Vector2f getOriginalPosition() { return new Vector2f(originalPosition); }
|
||||
public float getInfluenceRadius() { return influenceRadius; }
|
||||
public void setInfluenceRadius(float radius) { this.influenceRadius = radius; }
|
||||
public boolean isSelected() { return selected; }
|
||||
public void setSelected(boolean selected) { this.selected = selected; }
|
||||
public int getId() { return id; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public Map<Integer, Float> getWeightMap() { return new HashMap<>(weightMap); }
|
||||
public Vector2f getPosition() {
|
||||
return new Vector2f(position);
|
||||
}
|
||||
|
||||
public void setPosition(float x, float y) {
|
||||
this.position.set(x, y);
|
||||
}
|
||||
|
||||
public void setPosition(Vector2f pos) {
|
||||
this.position.set(pos);
|
||||
}
|
||||
|
||||
public Vector2f getOriginalPosition() {
|
||||
return new Vector2f(originalPosition);
|
||||
}
|
||||
|
||||
public float getInfluenceRadius() {
|
||||
return influenceRadius;
|
||||
}
|
||||
|
||||
public void setInfluenceRadius(float radius) {
|
||||
this.influenceRadius = radius;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Map<Integer, Float> getWeightMap() {
|
||||
return new HashMap<>(weightMap);
|
||||
}
|
||||
|
||||
public void move(float dx, float dy) {
|
||||
position.add(dx, dy);
|
||||
|
||||
@@ -5,8 +5,8 @@ import org.joml.Vector2f;
|
||||
/**
|
||||
* 工具类:用于在 Vector2f 和字符串之间进行转换。
|
||||
* 例如:
|
||||
* - toString(new Vector2f(1.5f, -2.0f)) => "1.5,-2.0"
|
||||
* - fromString("1.5,-2.0") => new Vector2f(1.5f, -2.0f)
|
||||
* - toString(new Vector2f(1.5f, -2.0f)) => "1.5,-2.0"
|
||||
* - fromString("1.5,-2.0") => new Vector2f(1.5f, -2.0f)
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
@@ -26,9 +26,9 @@ public class SaveVector2f {
|
||||
/**
|
||||
* 从字符串解析为 Vector2f。
|
||||
* 允许的格式:
|
||||
* - "x,y"
|
||||
* - "(x,y)"
|
||||
* - " x , y "
|
||||
* - "x,y"
|
||||
* - "(x,y)"
|
||||
* - " x , y "
|
||||
* 若格式错误则返回 (0,0)
|
||||
*/
|
||||
public static Vector2f fromString(String str) {
|
||||
|
||||
@@ -27,11 +27,25 @@ public class SecondaryVertex {
|
||||
}
|
||||
|
||||
// Getter和Setter方法
|
||||
public Vector2f getPosition() { return new Vector2f(position); }
|
||||
public Vector2f getOriginalPosition() { return new Vector2f(originalPosition); }
|
||||
public Vector2f getUV() { return new Vector2f(uv); }
|
||||
public boolean isSelected() { return selected; }
|
||||
public int getId() { return id; }
|
||||
public Vector2f getPosition() {
|
||||
return new Vector2f(position);
|
||||
}
|
||||
|
||||
public Vector2f getOriginalPosition() {
|
||||
return new Vector2f(originalPosition);
|
||||
}
|
||||
|
||||
public Vector2f getUV() {
|
||||
return new Vector2f(uv);
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setPosition(float x, float y) {
|
||||
this.position.set(x, y);
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.joml.Vector2f;
|
||||
|
||||
/**
|
||||
* 摄像机类
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class Camera {
|
||||
@@ -12,7 +13,8 @@ public class Camera {
|
||||
private float zPosition = 0.0f;
|
||||
private boolean enabled = true;
|
||||
|
||||
public Camera() {}
|
||||
public Camera() {
|
||||
}
|
||||
|
||||
public void setPosition(float x, float y) {
|
||||
position.set(x, y);
|
||||
|
||||
@@ -11,20 +11,19 @@ import java.nio.FloatBuffer;
|
||||
/**
|
||||
* 简化版 BufferBuilder,用于按顶点流构建并一次性绘制几何体。
|
||||
* 每个顶点格式: float x, float y, float u, float v (共4个 float)
|
||||
*
|
||||
* <p>
|
||||
* 用法:
|
||||
* BufferBuilder bb = new BufferBuilder();
|
||||
* bb.begin(GL11.GL_LINE_LOOP, 16);
|
||||
* bb.vertex(x,y,u,v);
|
||||
* ...
|
||||
* bb.end(); // 立即绘制并 cleanup
|
||||
*
|
||||
* BufferBuilder bb = new BufferBuilder();
|
||||
* bb.begin(GL11.GL_LINE_LOOP, 16);
|
||||
* bb.vertex(x,y,u,v);
|
||||
* ...
|
||||
* bb.end(); // 立即绘制并 cleanup
|
||||
* <p>
|
||||
* 设计原则:简单、可靠、方便把临时多顶点数据提交到 GPU。
|
||||
*
|
||||
*
|
||||
* @author tzdwindows
|
||||
* @version 1.2
|
||||
* @since 2025-10-16
|
||||
* @author tzdwindows
|
||||
*/
|
||||
public class BufferBuilder {
|
||||
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BufferBuilder.class);
|
||||
@@ -33,8 +32,9 @@ public class BufferBuilder {
|
||||
private int size; // float 数量
|
||||
private int vertexCount;
|
||||
private int mode; // GL mode
|
||||
private RenderState renderState = new RenderState();
|
||||
private final RenderState renderState = new RenderState();
|
||||
private boolean stateSaved = false;
|
||||
|
||||
// 内置状态,用于构建完成的缓冲区
|
||||
public static class BuiltBuffer {
|
||||
private final int vao;
|
||||
@@ -51,12 +51,25 @@ public class BufferBuilder {
|
||||
this.renderState = renderState;
|
||||
}
|
||||
|
||||
public RenderState renderState() { return renderState; }
|
||||
public RenderState renderState() {
|
||||
return renderState;
|
||||
}
|
||||
|
||||
public int vao() { return vao; }
|
||||
public int vbo() { return vbo; }
|
||||
public int vertexCount() { return vertexCount; }
|
||||
public int mode() { return mode; }
|
||||
public int vao() {
|
||||
return vao;
|
||||
}
|
||||
|
||||
public int vbo() {
|
||||
return vbo;
|
||||
}
|
||||
|
||||
public int vertexCount() {
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
public int mode() {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.lwjgl.opengl.GL20;
|
||||
/**
|
||||
* 缓冲区上传器
|
||||
*
|
||||
* @version 1.1 - 添加颜色支持
|
||||
* @author tzdwindows 7
|
||||
* @version 1.1 - 添加颜色支持
|
||||
*/
|
||||
public class BufferUploader {
|
||||
|
||||
@@ -65,7 +65,8 @@ public class BufferUploader {
|
||||
}
|
||||
if (currentProgram != 0) {
|
||||
int colorLoc = RenderSystem.getUniformLocation(currentProgram, "uColor");
|
||||
if (colorLoc == -1) {} else {
|
||||
if (colorLoc == -1) {
|
||||
} else {
|
||||
RenderSystem.uniform4f(colorLoc,
|
||||
state.color.x, state.color.y, state.color.z, state.color.w);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import com.chuangzhou.vivid2D.render.systems.RenderSystem;
|
||||
/**
|
||||
* 管理缓存
|
||||
*
|
||||
* @version 1.0
|
||||
* @author tzdwindows
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Tesselator {
|
||||
private static final int DEFAULT_BUFFER_SIZE = 2097152; // 2MB
|
||||
|
||||
@@ -3,12 +3,18 @@ package com.chuangzhou.vivid2D.render.systems.sources;
|
||||
/**
|
||||
* 完整着色器接口
|
||||
* 一个完整的着色器程序需要顶点着色器和片段着色器
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public interface CompleteShader {
|
||||
Shader getVertexShader();
|
||||
|
||||
Shader getFragmentShader();
|
||||
|
||||
String getShaderName();
|
||||
|
||||
boolean isDefaultShader();
|
||||
default void setDefaultUniforms(ShaderProgram program) {}
|
||||
|
||||
default void setDefaultUniforms(ShaderProgram program) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.chuangzhou.vivid2D.render.systems.sources;
|
||||
|
||||
/**
|
||||
* 着色器接口
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public interface Shader {
|
||||
String getShaderCode();
|
||||
|
||||
String getShaderName();
|
||||
}
|
||||
|
||||
@@ -8,104 +8,105 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
|
||||
public class FragmentShaders implements Shader {
|
||||
public static final String FRAGMENT_SHADER_SRC =
|
||||
"""
|
||||
#version 330 core
|
||||
in vec2 vTexCoord;
|
||||
in vec2 vWorldPos;
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform sampler2D uTexture;
|
||||
uniform vec4 uColor;
|
||||
uniform float uOpacity;
|
||||
uniform int uBlendMode;
|
||||
uniform int uDebugMode;
|
||||
|
||||
#define MAX_LIGHTS 8
|
||||
uniform vec2 uLightsPos[MAX_LIGHTS];
|
||||
uniform vec3 uLightsColor[MAX_LIGHTS];
|
||||
uniform float uLightsIntensity[MAX_LIGHTS];
|
||||
uniform int uLightsIsAmbient[MAX_LIGHTS];
|
||||
uniform int uLightCount;
|
||||
|
||||
// 常用衰减系数(可在 shader 内微调)
|
||||
const float ATT_CONST = 1.0;
|
||||
const float ATT_LINEAR = 0.09;
|
||||
const float ATT_QUAD = 0.032;
|
||||
|
||||
void main() {
|
||||
// 先采样纹理
|
||||
vec4 tex = texture(uTexture, vTexCoord);
|
||||
float alpha = tex.a * uOpacity;
|
||||
if (alpha <= 0.001) discard;
|
||||
|
||||
// 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色)
|
||||
if (uLightCount == 0) {
|
||||
vec3 base = tex.rgb * uColor.rgb;
|
||||
// 简单的色调映射(防止数值过大)
|
||||
base = clamp(base, 0.0, 1.0);
|
||||
FragColor = vec4(base, alpha);
|
||||
return;
|
||||
}
|
||||
|
||||
// 基础颜色(纹理 * 部件颜色)
|
||||
vec3 baseColor = tex.rgb * uColor.rgb;
|
||||
|
||||
// 全局环境光基线(可以适度提高以避免全黑)
|
||||
vec3 ambient = vec3(0.06); // 小环境光补偿
|
||||
vec3 lighting = vec3(0.0);
|
||||
vec3 specularAccum = vec3(0.0);
|
||||
|
||||
// 累积环境光(来自被标记为环境光的光源)
|
||||
for (int i = 0; i < uLightCount; ++i) {
|
||||
if (uLightsIsAmbient[i] == 1) {
|
||||
lighting += uLightsColor[i] * uLightsIntensity[i];
|
||||
#version 330 core
|
||||
in vec2 vTexCoord;
|
||||
in vec2 vWorldPos;
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform sampler2D uTexture;
|
||||
uniform vec4 uColor;
|
||||
uniform float uOpacity;
|
||||
uniform int uBlendMode;
|
||||
uniform int uDebugMode;
|
||||
|
||||
#define MAX_LIGHTS 8
|
||||
uniform vec2 uLightsPos[MAX_LIGHTS];
|
||||
uniform vec3 uLightsColor[MAX_LIGHTS];
|
||||
uniform float uLightsIntensity[MAX_LIGHTS];
|
||||
uniform int uLightsIsAmbient[MAX_LIGHTS];
|
||||
uniform int uLightCount;
|
||||
|
||||
// 常用衰减系数(可在 shader 内微调)
|
||||
const float ATT_CONST = 1.0;
|
||||
const float ATT_LINEAR = 0.09;
|
||||
const float ATT_QUAD = 0.032;
|
||||
|
||||
void main() {
|
||||
// 先采样纹理
|
||||
vec4 tex = texture(uTexture, vTexCoord);
|
||||
float alpha = tex.a * uOpacity;
|
||||
if (alpha <= 0.001) discard;
|
||||
|
||||
// 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色)
|
||||
if (uLightCount == 0) {
|
||||
vec3 base = tex.rgb * uColor.rgb;
|
||||
// 简单的色调映射(防止数值过大)
|
||||
base = clamp(base, 0.0, 1.0);
|
||||
FragColor = vec4(base, alpha);
|
||||
return;
|
||||
}
|
||||
|
||||
// 基础颜色(纹理 * 部件颜色)
|
||||
vec3 baseColor = tex.rgb * uColor.rgb;
|
||||
|
||||
// 全局环境光基线(可以适度提高以避免全黑)
|
||||
vec3 ambient = vec3(0.06); // 小环境光补偿
|
||||
vec3 lighting = vec3(0.0);
|
||||
vec3 specularAccum = vec3(0.0);
|
||||
|
||||
// 累积环境光(来自被标记为环境光的光源)
|
||||
for (int i = 0; i < uLightCount; ++i) {
|
||||
if (uLightsIsAmbient[i] == 1) {
|
||||
lighting += uLightsColor[i] * uLightsIntensity[i];
|
||||
}
|
||||
}
|
||||
// 加上基线环境光
|
||||
lighting += ambient;
|
||||
|
||||
// 对每个非环境光计算基于距离的衰减与简单高光
|
||||
for (int i = 0; i < uLightCount; ++i) {
|
||||
if (uLightsIsAmbient[i] == 1) continue;
|
||||
|
||||
vec2 toLight = uLightsPos[i] - vWorldPos;
|
||||
float dist = length(toLight);
|
||||
// 标准物理式衰减
|
||||
float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist);
|
||||
|
||||
// 强度受光源强度和衰减影响
|
||||
float radiance = uLightsIntensity[i] * attenuation;
|
||||
|
||||
// 漫反射:在纯2D情景下,法线与视线近似固定(Z向),
|
||||
// 所以漫反射对所有片元是恒定的。我们用一个基于距离的柔和因子来模拟明暗变化。
|
||||
float diffuseFactor = clamp(1.0 - (dist * 0.0015), 0.0, 1.0); // 通过调节常数控制半径感觉
|
||||
vec3 diff = uLightsColor[i] * radiance * diffuseFactor;
|
||||
lighting += diff;
|
||||
|
||||
// 简单高光(基于视向与反射的大致模拟,产生亮点)
|
||||
vec3 lightDir3 = normalize(vec3(toLight, 0.0));
|
||||
vec3 viewDir = vec3(0.0, 0.0, 1.0);
|
||||
vec3 normal = vec3(0.0, 0.0, 1.0);
|
||||
vec3 reflectDir = reflect(-lightDir3, normal);
|
||||
float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); // 16 为高光粗糙度,可调
|
||||
float specIntensity = 0.2; // 高光强度系数
|
||||
specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity;
|
||||
}
|
||||
|
||||
// 限制光照的最大值以避免过曝(可根据场景调整)
|
||||
vec3 totalLighting = min(lighting + specularAccum, vec3(2.0));
|
||||
|
||||
// 将光照应用到基础颜色
|
||||
vec3 finalColor = baseColor * totalLighting;
|
||||
|
||||
// 支持简单混合模式(保留原有行为)
|
||||
if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb;
|
||||
else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb;
|
||||
else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb);
|
||||
|
||||
finalColor = clamp(finalColor, 0.0, 1.0);
|
||||
FragColor = vec4(finalColor, alpha);
|
||||
}
|
||||
}
|
||||
// 加上基线环境光
|
||||
lighting += ambient;
|
||||
|
||||
// 对每个非环境光计算基于距离的衰减与简单高光
|
||||
for (int i = 0; i < uLightCount; ++i) {
|
||||
if (uLightsIsAmbient[i] == 1) continue;
|
||||
|
||||
vec2 toLight = uLightsPos[i] - vWorldPos;
|
||||
float dist = length(toLight);
|
||||
// 标准物理式衰减
|
||||
float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist);
|
||||
|
||||
// 强度受光源强度和衰减影响
|
||||
float radiance = uLightsIntensity[i] * attenuation;
|
||||
|
||||
// 漫反射:在纯2D情景下,法线与视线近似固定(Z向),
|
||||
// 所以漫反射对所有片元是恒定的。我们用一个基于距离的柔和因子来模拟明暗变化。
|
||||
float diffuseFactor = clamp(1.0 - (dist * 0.0015), 0.0, 1.0); // 通过调节常数控制半径感觉
|
||||
vec3 diff = uLightsColor[i] * radiance * diffuseFactor;
|
||||
lighting += diff;
|
||||
|
||||
// 简单高光(基于视向与反射的大致模拟,产生亮点)
|
||||
vec3 lightDir3 = normalize(vec3(toLight, 0.0));
|
||||
vec3 viewDir = vec3(0.0, 0.0, 1.0);
|
||||
vec3 normal = vec3(0.0, 0.0, 1.0);
|
||||
vec3 reflectDir = reflect(-lightDir3, normal);
|
||||
float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); // 16 为高光粗糙度,可调
|
||||
float specIntensity = 0.2; // 高光强度系数
|
||||
specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity;
|
||||
}
|
||||
|
||||
// 限制光照的最大值以避免过曝(可根据场景调整)
|
||||
vec3 totalLighting = min(lighting + specularAccum, vec3(2.0));
|
||||
|
||||
// 将光照应用到基础颜色
|
||||
vec3 finalColor = baseColor * totalLighting;
|
||||
|
||||
// 支持简单混合模式(保留原有行为)
|
||||
if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb;
|
||||
else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb;
|
||||
else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb);
|
||||
|
||||
finalColor = clamp(finalColor, 0.0, 1.0);
|
||||
FragColor = vec4(finalColor, alpha);
|
||||
}
|
||||
""";
|
||||
""";
|
||||
|
||||
@Override
|
||||
public String getShaderCode() {
|
||||
return FRAGMENT_SHADER_SRC;
|
||||
|
||||
@@ -5,28 +5,29 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
|
||||
/**
|
||||
* 纯色着色器的片段着色器
|
||||
* 只使用颜色,忽略纹理
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class SolidColorFragmentShader implements Shader {
|
||||
public static final String FRAGMENT_SHADER_SRC =
|
||||
"""
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform vec4 uColor;
|
||||
uniform float uOpacity;
|
||||
|
||||
void main() {
|
||||
// 直接使用颜色,忽略纹理
|
||||
vec4 finalColor = uColor;
|
||||
finalColor.a *= uOpacity;
|
||||
|
||||
// 如果透明度太低则丢弃片段
|
||||
if (finalColor.a <= 0.001) discard;
|
||||
|
||||
FragColor = finalColor;
|
||||
}
|
||||
""";
|
||||
#version 330 core
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform vec4 uColor;
|
||||
uniform float uOpacity;
|
||||
|
||||
void main() {
|
||||
// 直接使用颜色,忽略纹理
|
||||
vec4 finalColor = uColor;
|
||||
finalColor.a *= uOpacity;
|
||||
|
||||
// 如果透明度太低则丢弃片段
|
||||
if (finalColor.a <= 0.001) discard;
|
||||
|
||||
FragColor = finalColor;
|
||||
}
|
||||
""";
|
||||
|
||||
@Override
|
||||
public String getShaderCode() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
|
||||
/**
|
||||
* 纯色着色器程序
|
||||
* 专门用于绘制纯色几何体,如选中框、调试图形等
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class SolidColorShader implements CompleteShader {
|
||||
|
||||
@@ -4,25 +4,26 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
|
||||
|
||||
/**
|
||||
* 纯色着色器的顶点着色器
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class SolidColorVertexShader implements Shader {
|
||||
public static final String VERTEX_SHADER_SRC =
|
||||
"""
|
||||
#version 330 core
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
|
||||
uniform mat3 uModelMatrix;
|
||||
uniform mat3 uViewMatrix;
|
||||
uniform mat3 uProjectionMatrix;
|
||||
|
||||
void main() {
|
||||
// 使用 3x3 矩阵链计算屏幕位置
|
||||
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
|
||||
gl_Position = vec4(p.xy, 0.0, 1.0);
|
||||
}
|
||||
""";
|
||||
#version 330 core
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
|
||||
uniform mat3 uModelMatrix;
|
||||
uniform mat3 uViewMatrix;
|
||||
uniform mat3 uProjectionMatrix;
|
||||
|
||||
void main() {
|
||||
// 使用 3x3 矩阵链计算屏幕位置
|
||||
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
|
||||
gl_Position = vec4(p.xy, 0.0, 1.0);
|
||||
}
|
||||
""";
|
||||
|
||||
@Override
|
||||
public String getShaderCode() {
|
||||
|
||||
@@ -12,25 +12,25 @@ import com.chuangzhou.vivid2D.render.systems.sources.Shader;
|
||||
public class VertexShaders implements Shader {
|
||||
public static final String VERTEX_SHADER_SRC =
|
||||
"""
|
||||
#version 330 core
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
out vec2 vTexCoord;
|
||||
out vec2 vWorldPos;
|
||||
|
||||
uniform mat3 uModelMatrix;
|
||||
uniform mat3 uViewMatrix;
|
||||
uniform mat3 uProjectionMatrix;
|
||||
|
||||
void main() {
|
||||
// 使用 3x3 矩阵链计算屏幕位置(假设矩阵是二维仿射)
|
||||
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
|
||||
gl_Position = vec4(p.xy, 0.0, 1.0);
|
||||
vTexCoord = aTexCoord;
|
||||
// 输出 world-space 位置供 fragment shader 使用(仅 xy)
|
||||
vWorldPos = (uModelMatrix * vec3(aPosition, 1.0)).xy;
|
||||
}
|
||||
""";
|
||||
#version 330 core
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
out vec2 vTexCoord;
|
||||
out vec2 vWorldPos;
|
||||
|
||||
uniform mat3 uModelMatrix;
|
||||
uniform mat3 uViewMatrix;
|
||||
uniform mat3 uProjectionMatrix;
|
||||
|
||||
void main() {
|
||||
// 使用 3x3 矩阵链计算屏幕位置(假设矩阵是二维仿射)
|
||||
vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0);
|
||||
gl_Position = vec4(p.xy, 0.0, 1.0);
|
||||
vTexCoord = aTexCoord;
|
||||
// 输出 world-space 位置供 fragment shader 使用(仅 xy)
|
||||
vWorldPos = (uModelMatrix * vec3(aPosition, 1.0)).xy;
|
||||
}
|
||||
""";
|
||||
|
||||
@Override
|
||||
public String getShaderCode() {
|
||||
|
||||
@@ -32,7 +32,8 @@ public class ModelLayerPanelTest {
|
||||
if (person != null) {
|
||||
try {
|
||||
person.setOpacity(0.85f);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建 UI
|
||||
@@ -80,7 +81,8 @@ public class ModelLayerPanelTest {
|
||||
// 同步通知渲染面板(如果需要)去刷新模型
|
||||
try {
|
||||
renderPanel.setModel(model);
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
});
|
||||
bottom.add(refreshBtn);
|
||||
|
||||
@@ -138,7 +140,8 @@ public class ModelLayerPanelTest {
|
||||
// 进程退出(确保彻底关闭)
|
||||
try {
|
||||
renderPanel.dispose();
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -16,11 +16,11 @@ import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
/**
|
||||
* ModelLoadTest - enhanced debug loader
|
||||
*
|
||||
* <p>
|
||||
* - 加载模型后会自动检查 model 内部结构并打印(parts, meshes, textures)
|
||||
* - 尝试把第一个 part 放到窗口中心以确保在可视范围内
|
||||
* - 每帧确保 ModelRender 的 viewport 与窗口大小一致
|
||||
*
|
||||
* <p>
|
||||
* 运行前请确保 MODEL_PATH 指向你保存的 model 文件
|
||||
*/
|
||||
public class ModelLoadTest {
|
||||
@@ -115,7 +115,7 @@ public class ModelLoadTest {
|
||||
* 其次尝试创建空实例并调用实例方法 loadFromFile(String)
|
||||
*/
|
||||
private void loadModelFromFile(String path) {
|
||||
inspectSerializedFileStructure( path);
|
||||
inspectSerializedFileStructure(path);
|
||||
File f = new File(path);
|
||||
if (!f.exists()) {
|
||||
System.err.println("Model file not found: " + path);
|
||||
@@ -165,7 +165,8 @@ public class ModelLoadTest {
|
||||
model = inst;
|
||||
System.out.println("Model loaded via instance method loadFromFile");
|
||||
return;
|
||||
} catch (NoSuchMethodException ignored) { }
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
@@ -209,35 +210,50 @@ public class ModelLoadTest {
|
||||
|
||||
private void printObjectStructure(Object obj, int indent, java.util.Set<Object> seen) {
|
||||
if (obj == null) {
|
||||
printIndent(indent); System.out.println("null"); return;
|
||||
printIndent(indent);
|
||||
System.out.println("null");
|
||||
return;
|
||||
}
|
||||
if (seen.contains(obj)) {
|
||||
printIndent(indent); System.out.println("<<already seen " + obj.getClass().getName() + ">>"); return;
|
||||
printIndent(indent);
|
||||
System.out.println("<<already seen " + obj.getClass().getName() + ">>");
|
||||
return;
|
||||
}
|
||||
seen.add(obj);
|
||||
Class<?> cls = obj.getClass();
|
||||
printIndent(indent); System.out.println(cls.getName());
|
||||
printIndent(indent);
|
||||
System.out.println(cls.getName());
|
||||
// 如果是集合,列出元素类型/数目(不深入过深避免堆栈)
|
||||
if (obj instanceof java.util.Collection) {
|
||||
java.util.Collection col = (java.util.Collection) obj;
|
||||
printIndent(indent+1); System.out.println("size=" + col.size());
|
||||
if (obj instanceof java.util.Collection col) {
|
||||
printIndent(indent + 1);
|
||||
System.out.println("size=" + col.size());
|
||||
int i = 0;
|
||||
for (Object e : col) {
|
||||
if (i++ > 20) { printIndent(indent+1); System.out.println("... (truncated)"); break; }
|
||||
printObjectStructure(e, indent+1, seen);
|
||||
if (i++ > 20) {
|
||||
printIndent(indent + 1);
|
||||
System.out.println("... (truncated)");
|
||||
break;
|
||||
}
|
||||
printObjectStructure(e, indent + 1, seen);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (obj instanceof java.util.Map) {
|
||||
java.util.Map map = (java.util.Map) obj;
|
||||
printIndent(indent+1); System.out.println("size=" + map.size());
|
||||
if (obj instanceof java.util.Map map) {
|
||||
printIndent(indent + 1);
|
||||
System.out.println("size=" + map.size());
|
||||
int i = 0;
|
||||
for (Object k : map.keySet()) {
|
||||
if (i++ > 20) { printIndent(indent+1); System.out.println("... (truncated)"); break; }
|
||||
printIndent(indent+1); System.out.println("Key:");
|
||||
printObjectStructure(k, indent+2, seen);
|
||||
printIndent(indent+1); System.out.println("Value:");
|
||||
printObjectStructure(map.get(k), indent+2, seen);
|
||||
if (i++ > 20) {
|
||||
printIndent(indent + 1);
|
||||
System.out.println("... (truncated)");
|
||||
break;
|
||||
}
|
||||
printIndent(indent + 1);
|
||||
System.out.println("Key:");
|
||||
printObjectStructure(k, indent + 2, seen);
|
||||
printIndent(indent + 1);
|
||||
System.out.println("Value:");
|
||||
printObjectStructure(map.get(k), indent + 2, seen);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -246,12 +262,16 @@ public class ModelLoadTest {
|
||||
for (java.lang.reflect.Field f : fields) {
|
||||
f.setAccessible(true);
|
||||
Object val = null;
|
||||
try { val = f.get(obj); } catch (Throwable ignored) {}
|
||||
printIndent(indent+1); System.out.println(f.getName() + " : " + (val == null ? "null" : val.getClass().getName()));
|
||||
try {
|
||||
val = f.get(obj);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
printIndent(indent + 1);
|
||||
System.out.println(f.getName() + " : " + (val == null ? "null" : val.getClass().getName()));
|
||||
// 对常见自定义类型深入一层
|
||||
if (val != null && !val.getClass().getName().startsWith("java.") && !val.getClass().isPrimitive()) {
|
||||
if (val instanceof Number || val instanceof String) continue;
|
||||
printObjectStructure(val, indent+2, seen);
|
||||
printObjectStructure(val, indent + 2, seen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,8 +303,7 @@ public class ModelLoadTest {
|
||||
|
||||
if (getParts != null) {
|
||||
Object partsObj = getParts.invoke(model);
|
||||
if (partsObj instanceof List) {
|
||||
List<?> parts = (List<?>) partsObj;
|
||||
if (partsObj instanceof List<?> parts) {
|
||||
System.out.println("Parts count: " + parts.size());
|
||||
for (int i = 0; i < parts.size(); i++) {
|
||||
Object p = parts.get(i);
|
||||
@@ -296,7 +315,8 @@ public class ModelLoadTest {
|
||||
Object name = getName != null ? getName.invoke(p) : "<no-name>";
|
||||
Object pos = getPosition != null ? getPosition.invoke(p) : null;
|
||||
System.out.println(" name=" + name + ", pos=" + pos);
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 parts 不为空,尝试把第一个 part 放到窗口中心(如果有 setPosition)
|
||||
@@ -305,7 +325,7 @@ public class ModelLoadTest {
|
||||
try {
|
||||
Method setPosition = tryGetMethod(first.getClass(), "setPosition", float.class, float.class);
|
||||
if (setPosition != null) {
|
||||
setPosition.invoke(first, (float)WINDOW_WIDTH/2f, (float)WINDOW_HEIGHT/2f);
|
||||
setPosition.invoke(first, (float) WINDOW_WIDTH / 2f, (float) WINDOW_HEIGHT / 2f);
|
||||
System.out.println("Moved first part to window center.");
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
@@ -317,8 +337,7 @@ public class ModelLoadTest {
|
||||
|
||||
if (getMeshes != null) {
|
||||
Object meshesObj = getMeshes.invoke(model);
|
||||
if (meshesObj instanceof List) {
|
||||
List<?> meshes = (List<?>) meshesObj;
|
||||
if (meshesObj instanceof List<?> meshes) {
|
||||
System.out.println("Meshes count: " + meshes.size());
|
||||
for (int i = 0; i < Math.min(meshes.size(), 10); i++) {
|
||||
Object m = meshes.get(i);
|
||||
@@ -330,15 +349,15 @@ public class ModelLoadTest {
|
||||
Object vc = getVertexCount != null ? getVertexCount.invoke(m) : null;
|
||||
Object tex = getTexture != null ? getTexture.invoke(m) : null;
|
||||
System.out.println(" vertexCount=" + vc + ", texture=" + tex);
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getTextures != null) {
|
||||
Object texObj = getTextures.invoke(model);
|
||||
if (texObj instanceof List) {
|
||||
List<?> texs = (List<?>) texObj;
|
||||
if (texObj instanceof List<?> texs) {
|
||||
System.out.println("Textures count: " + texs.size());
|
||||
for (int i = 0; i < Math.min(texs.size(), 10); i++) {
|
||||
Object t = texs.get(i);
|
||||
@@ -351,7 +370,8 @@ public class ModelLoadTest {
|
||||
Object h = getH != null ? getH.invoke(t) : "?";
|
||||
Object id = getId != null ? getId.invoke(t) : "?";
|
||||
System.out.println(" size=" + w + "x" + h + ", id=" + id);
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,8 +457,8 @@ public class ModelLoadTest {
|
||||
|
||||
try {
|
||||
ModelRender.cleanup();
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
|
||||
if (window != MemoryUtil.NULL) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Texture;
|
||||
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
@@ -22,6 +21,7 @@ import java.util.Random;
|
||||
/**
|
||||
* ModelRenderLightingTest
|
||||
* 测试使用 Model2D + 光源进行简单光照渲染
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ModelRenderLightingTest {
|
||||
@@ -34,7 +34,7 @@ public class ModelRenderLightingTest {
|
||||
private boolean running = true;
|
||||
|
||||
private Model2D model;
|
||||
private Random random = new Random();
|
||||
private final Random random = new Random();
|
||||
|
||||
private float animationTime = 0f;
|
||||
|
||||
@@ -115,7 +115,7 @@ public class ModelRenderLightingTest {
|
||||
rightArm.setPosition(60, -20);
|
||||
Mesh2D rightArmMesh = Mesh2D.createQuad("right_arm_mesh", 18, 90);
|
||||
rightArmMesh.setTexture(createSolidTexture(16, 90, 0xFF6495ED));
|
||||
rightArmMesh.setSelected( true);
|
||||
rightArmMesh.setSelected(true);
|
||||
rightArm.addMesh(rightArmMesh);
|
||||
|
||||
// legs
|
||||
@@ -152,11 +152,11 @@ public class ModelRenderLightingTest {
|
||||
|
||||
private Texture createSolidTexture(int w, int h, int rgba) {
|
||||
ByteBuffer buf = MemoryUtil.memAlloc(w * h * 4);
|
||||
byte a = (byte)((rgba >> 24) & 0xFF);
|
||||
byte r = (byte)((rgba >> 16) & 0xFF);
|
||||
byte g = (byte)((rgba >> 8) & 0xFF);
|
||||
byte b = (byte)(rgba & 0xFF);
|
||||
for(int i=0;i<w*h;i++) buf.put(r).put(g).put(b).put(a);
|
||||
byte a = (byte) ((rgba >> 24) & 0xFF);
|
||||
byte r = (byte) ((rgba >> 16) & 0xFF);
|
||||
byte g = (byte) ((rgba >> 8) & 0xFF);
|
||||
byte b = (byte) (rgba & 0xFF);
|
||||
for (int i = 0; i < w * h; i++) buf.put(r).put(g).put(b).put(a);
|
||||
buf.flip();
|
||||
Texture t = new Texture("solid_" + rgba, w, h, Texture.TextureFormat.RGBA, buf);
|
||||
MemoryUtil.memFree(buf);
|
||||
@@ -166,16 +166,16 @@ public class ModelRenderLightingTest {
|
||||
private Texture createHeadTexture() {
|
||||
int width = 64, height = 64;
|
||||
int[] pixels = new int[width * height];
|
||||
for(int y=0;y<height;y++) {
|
||||
for(int x=0;x<width;x++) {
|
||||
float dx = (x - width/2f)/(width/2f);
|
||||
float dy = (y - height/2f)/(height/2f);
|
||||
float dist = (float)Math.sqrt(dx*dx + dy*dy);
|
||||
int alpha = dist>1.0f?0:255;
|
||||
int r = (int)(240*(1f - dist*0.25f));
|
||||
int g = (int)(200*(1f - dist*0.25f));
|
||||
int b = (int)(180*(1f - dist*0.25f));
|
||||
pixels[y*width + x] = (alpha<<24)|(r<<16)|(g<<8)|b;
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
float dx = (x - width / 2f) / (width / 2f);
|
||||
float dy = (y - height / 2f) / (height / 2f);
|
||||
float dist = (float) Math.sqrt(dx * dx + dy * dy);
|
||||
int alpha = dist > 1.0f ? 0 : 255;
|
||||
int r = (int) (240 * (1f - dist * 0.25f));
|
||||
int g = (int) (200 * (1f - dist * 0.25f));
|
||||
int b = (int) (180 * (1f - dist * 0.25f));
|
||||
pixels[y * width + x] = (alpha << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
return new Texture("head_tex", width, height, Texture.TextureFormat.RGBA, pixels);
|
||||
@@ -188,11 +188,11 @@ public class ModelRenderLightingTest {
|
||||
|
||||
while (running && !GLFW.glfwWindowShouldClose(window)) {
|
||||
long now = System.nanoTime();
|
||||
accumulator += (now - last)/nsPerUpdate;
|
||||
accumulator += (now - last) / nsPerUpdate;
|
||||
last = now;
|
||||
|
||||
while (accumulator >= 1.0) {
|
||||
update(1.0f/60.0f);
|
||||
update(1.0f / 60.0f);
|
||||
accumulator -= 1.0;
|
||||
}
|
||||
|
||||
@@ -204,9 +204,9 @@ public class ModelRenderLightingTest {
|
||||
|
||||
private void update(float dt) {
|
||||
animationTime += dt;
|
||||
float armSwing = (float)Math.sin(animationTime*3f)*0.7f;
|
||||
float legSwing = (float)Math.sin(animationTime*3f + Math.PI)*0.6f;
|
||||
float headRot = (float)Math.sin(animationTime*1.4f)*0.15f;
|
||||
float armSwing = (float) Math.sin(animationTime * 3f) * 0.7f;
|
||||
float legSwing = (float) Math.sin(animationTime * 3f + Math.PI) * 0.6f;
|
||||
float headRot = (float) Math.sin(animationTime * 1.4f) * 0.15f;
|
||||
|
||||
ModelPart leftArm = model.getPart("left_arm");
|
||||
ModelPart rightArm = model.getPart("right_arm");
|
||||
@@ -214,24 +214,24 @@ public class ModelRenderLightingTest {
|
||||
ModelPart rightLeg = model.getPart("right_leg");
|
||||
ModelPart head = model.getPart("head");
|
||||
|
||||
if(leftArm!=null) leftArm.setRotation(-0.8f*armSwing - 0.2f);
|
||||
if(rightArm!=null) rightArm.setRotation(0.8f*armSwing + 0.2f);
|
||||
if(leftLeg!=null) leftLeg.setRotation(0.6f*legSwing);
|
||||
if(rightLeg!=null) rightLeg.setRotation(-0.6f*legSwing);
|
||||
if(head!=null) head.setRotation(headRot);
|
||||
if (leftArm != null) leftArm.setRotation(-0.8f * armSwing - 0.2f);
|
||||
if (rightArm != null) rightArm.setRotation(0.8f * armSwing + 0.2f);
|
||||
if (leftLeg != null) leftLeg.setRotation(0.6f * legSwing);
|
||||
if (rightLeg != null) rightLeg.setRotation(-0.6f * legSwing);
|
||||
if (head != null) head.setRotation(headRot);
|
||||
|
||||
model.update(dt);
|
||||
}
|
||||
|
||||
private void render() {
|
||||
RenderSystem.setClearColor(0.18f,0.18f,0.25f,1.0f);
|
||||
ModelRender.render(1f/60f, model);
|
||||
RenderSystem.setClearColor(0.18f, 0.18f, 0.25f, 1.0f);
|
||||
ModelRender.render(1f / 60f, model);
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
ModelRender.cleanup();
|
||||
Texture.cleanupAll();
|
||||
if(window!= MemoryUtil.NULL) GLFW.glfwDestroyWindow(window);
|
||||
if (window != MemoryUtil.NULL) GLFW.glfwDestroyWindow(window);
|
||||
GLFW.glfwTerminate();
|
||||
GLFW.glfwSetErrorCallback(null).free();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.Random;
|
||||
* 重写后的 ModelRender 测试示例:构造一个简单的人形(头、身体、左右手、左右腿)
|
||||
* 便于验证层级变换与渲染是否正确。
|
||||
*
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ModelRenderTest {
|
||||
@@ -34,7 +33,7 @@ public class ModelRenderTest {
|
||||
private boolean running = true;
|
||||
|
||||
private Model2D testModel;
|
||||
private Random random = new Random();
|
||||
private final Random random = new Random();
|
||||
|
||||
private float animationTime = 0f;
|
||||
private boolean animate = true;
|
||||
|
||||
@@ -6,8 +6,6 @@ import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Texture;
|
||||
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector2f;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
@@ -18,6 +16,7 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* 用于测试中心点旋转
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ModelRenderTest2 {
|
||||
@@ -32,6 +31,7 @@ public class ModelRenderTest2 {
|
||||
private float rotationAngle = 0f;
|
||||
private int testCase = 0;
|
||||
private Mesh2D squareMesh;
|
||||
|
||||
public static void main(String[] args) {
|
||||
new ModelRenderTest2().run();
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public class ModelRenderTest2 {
|
||||
|
||||
ModelPart square = testModel.createPart("square");
|
||||
square.setPosition(0, 0); // center of window
|
||||
square.setPivot(0,0);
|
||||
square.setPivot(0, 0);
|
||||
// Create 80x80 quad centered at origin
|
||||
squareMesh = Mesh2D.createQuad("square_mesh", 80, 80);
|
||||
// Shift vertices so center is at (0,0)
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.chuangzhou.vivid2D.render.model.Model2D;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Texture;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
@@ -15,6 +14,7 @@ import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
/**
|
||||
* Texture Render Test Class - Debug Version
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ModelRenderTextureTest {
|
||||
@@ -143,10 +143,10 @@ public class ModelRenderTextureTest {
|
||||
// 回退到手动创建顶点
|
||||
System.out.println("Using manual vertex creation");
|
||||
float[] vertices = {
|
||||
-width/2, -height/2, // bottom left
|
||||
width/2, -height/2, // bottom right
|
||||
width/2, height/2, // top right
|
||||
-width/2, height/2 // top left
|
||||
-width / 2, -height / 2, // bottom left
|
||||
width / 2, -height / 2, // bottom right
|
||||
width / 2, height / 2, // top right
|
||||
-width / 2, height / 2 // top left
|
||||
};
|
||||
|
||||
float[] uvs = {
|
||||
@@ -203,10 +203,10 @@ public class ModelRenderTextureTest {
|
||||
} catch (Exception e) {
|
||||
// 手动创建
|
||||
float[] vertices = {
|
||||
-width/2, -height/2,
|
||||
width/2, -height/2,
|
||||
width/2, height/2,
|
||||
-width/2, height/2
|
||||
-width / 2, -height / 2,
|
||||
width / 2, -height / 2,
|
||||
width / 2, height / 2,
|
||||
-width / 2, height / 2
|
||||
};
|
||||
|
||||
float[] uvs = {
|
||||
@@ -320,11 +320,16 @@ public class ModelRenderTextureTest {
|
||||
|
||||
private String getGLErrorString(int error) {
|
||||
switch (error) {
|
||||
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
|
||||
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
|
||||
default: return "Unknown Error (0x" + Integer.toHexString(error) + ")";
|
||||
case GL_INVALID_ENUM:
|
||||
return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE:
|
||||
return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION:
|
||||
return "GL_INVALID_OPERATION";
|
||||
case GL_OUT_OF_MEMORY:
|
||||
return "GL_OUT_OF_MEMORY";
|
||||
default:
|
||||
return "Unknown Error (0x" + Integer.toHexString(error) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.chuangzhou.vivid2D.test;
|
||||
|
||||
import com.chuangzhou.vivid2D.render.model.AnimationParameter;
|
||||
import com.chuangzhou.vivid2D.render.model.Model2D;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.model.AnimationParameter;
|
||||
import com.chuangzhou.vivid2D.render.model.transform.WaveDeformer;
|
||||
import com.chuangzhou.vivid2D.render.model.util.*;
|
||||
import org.joml.Vector2f;
|
||||
@@ -809,7 +809,6 @@ public class ModelTest {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Utility method to print part hierarchy
|
||||
*/
|
||||
|
||||
@@ -4,8 +4,8 @@ import com.chuangzhou.vivid2D.render.ModelRender;
|
||||
import com.chuangzhou.vivid2D.render.model.Model2D;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Texture;
|
||||
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Texture;
|
||||
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
|
||||
import org.joml.Vector2f;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 物理系统使用实例 - 演示弹簧、重力和碰撞效果
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class ModelTest2 {
|
||||
@@ -39,7 +40,7 @@ public class ModelTest2 {
|
||||
private boolean springsEnabled = true;
|
||||
|
||||
// 存储部件引用,用于清理
|
||||
private List<ModelPart> currentParts = new ArrayList<>();
|
||||
private final List<ModelPart> currentParts = new ArrayList<>();
|
||||
|
||||
// 所有测试基点(初始 xy = 0,0)
|
||||
private final Vector2f initialOrigin = new Vector2f(0, 0);
|
||||
@@ -598,13 +599,20 @@ public class ModelTest2 {
|
||||
*/
|
||||
private String getTestCaseName(int testCase) {
|
||||
switch (testCase) {
|
||||
case 0: return "Spring Chain";
|
||||
case 1: return "Cloth Simulation";
|
||||
case 2: return "Pendulum System";
|
||||
case 3: return "Soft Body";
|
||||
case 4: return "Wind Test";
|
||||
case 5: return "Free Fall Test";
|
||||
default: return "Unknown";
|
||||
case 0:
|
||||
return "Spring Chain";
|
||||
case 1:
|
||||
return "Cloth Simulation";
|
||||
case 2:
|
||||
return "Pendulum System";
|
||||
case 3:
|
||||
return "Soft Body";
|
||||
case 4:
|
||||
return "Wind Test";
|
||||
case 5:
|
||||
return "Free Fall Test";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* 在原 TestModelGLPanel 的基础上增加简单动画(手臂、腿、头部摆动)
|
||||
*
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class TestModelGLPanel {
|
||||
|
||||
Reference in New Issue
Block a user