Files
window-axis-innovators-box/src/main/java/com/chuangzhou/vivid2D/test/TestModelGLPanel.java
tzdwindows 7 1bc2634afb feat(render):重构 ModelGLPanel与 ModelRender 并增强渲染功能
- 重构 ModelGLPanel 支持动态尺寸调整和离屏渲染上下文重建
- 添加 GL 上下文任务队列机制,支持线程安全的 OpenGL 操作- 引入 SLF4J 日志系统替换原有 System.out 输出
- 优化像素读取逻辑,支持 ARGB 格式与图像缓冲复用- 增强错误处理与资源清理逻辑,提升稳定性
- 完善 Model2D与 ModelRender 类的文档注释与结构定义
- 新增 TestModelGLPanel 动画示例,展示模型部件控制与物理系统应用
2025-10-13 22:12:30 +08:00

211 lines
8.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.chuangzhou.vivid2D.test;
import com.chuangzhou.vivid2D.render.ModelGLPanel;
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.PhysicsSystem;
import com.chuangzhou.vivid2D.render.model.util.Texture;
import org.joml.Vector2f;
import org.lwjgl.system.MemoryUtil;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.nio.ByteBuffer;
/**
* 在原 TestModelGLPanel 的基础上增加简单动画(手臂、腿、头部摆动)
* @author tzdwindows 7
*/
public class TestModelGLPanel {
private static final String MODEL_PATH = "C:\\Users\\Administrator\\Desktop\\trump_texture.model";
// 使 testModel 与动画计时可访问
private static Model2D testModel;
private static float animationTime = 0f;
private static boolean animate = true;
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("ModelGLPanel Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ModelGLPanel glPanel = null;
try {
// 先创建一个空的 Model2D 实例(将在 GL 上下文中初始化更详细内容)
testModel = new Model2D("Humanoid");
glPanel = new ModelGLPanel(testModel, 800, 600);
// 在 GL 上下文中创建 mesh / part / physics 等资源
ModelGLPanel finalGlPanel = glPanel;
glPanel.executeInGLContext(() -> {
setupModelInGL(testModel);
return null;
});
// 创建一个 Swing Timer用于驱动动画~60 FPS
int fps = 60;
int delayMs = 1000 / fps;
Timer timer = new Timer(delayMs, (ActionEvent e) -> {
if (!animate) return;
float dt = 1.0f / fps;
// 在 GL 上下文中更新模型状态(旋转、参数、物理更新等)
finalGlPanel.executeInGLContext(() -> {
updateAnimation(testModel, dt);
return null;
});
// 请求重绘ModelGLPanel 应在其 paintGL 中处理渲染)
finalGlPanel.repaint();
});
timer.start();
// 可选在窗口上添加键盘控制开关Space 切换动画)
frame.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyReleased(java.awt.event.KeyEvent e) {
if (e.getKeyCode() == java.awt.event.KeyEvent.VK_SPACE) {
animate = !animate;
System.out.println("Animation " + (animate ? "enabled" : "disabled"));
}
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
// 将 GL 面板加入窗体并显示
frame.add(glPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
private static void setupModelInGL(Model2D model) {
PhysicsSystem physics = model.getPhysics();
physics.setGravity(new Vector2f(0, -98.0f));
physics.setAirResistance(0.05f);
physics.setTimeScale(1.0f);
physics.setEnabled(true);
physics.initialize();
// body 放在屏幕中心
ModelPart body = model.createPart("body");
body.setPosition(0, 0);
// 身体网格:宽 80 高 120
Mesh2D bodyMesh = Mesh2D.createQuad("body_mesh", 80, 120);
bodyMesh.setTexture(createSolidTexture(64, 128, 0xFF4A6AFF)); // 蓝衣
body.addMesh(bodyMesh);
// head相对于 body 在上方偏移
ModelPart head = model.createPart("head");
head.setPosition(0, -90);
Mesh2D headMesh = Mesh2D.createQuad("head_mesh", 60, 60);
headMesh.setTexture(createHeadTexture());
head.addMesh(headMesh);
// left arm
ModelPart leftArm = model.createPart("left_arm");
leftArm.setPosition(-60, -20);
Mesh2D leftArmMesh = Mesh2D.createQuad("left_arm_mesh", 18, 90);
leftArmMesh.setTexture(createSolidTexture(16, 90, 0xFF6495ED));
leftArm.addMesh(leftArmMesh);
// right arm
ModelPart rightArm = model.createPart("right_arm");
rightArm.setPosition(60, -20);
Mesh2D rightArmMesh = Mesh2D.createQuad("right_arm_mesh", 18, 90);
rightArmMesh.setTexture(createSolidTexture(16, 90, 0xFF6495ED));
rightArm.addMesh(rightArmMesh);
// left leg
ModelPart leftLeg = model.createPart("left_leg");
leftLeg.setPosition(-20, 90);
Mesh2D leftLegMesh = Mesh2D.createQuad("left_leg_mesh", 20, 100);
leftLegMesh.setTexture(createSolidTexture(20, 100, 0xFF4169E1));
leftLeg.addMesh(leftLegMesh);
// right leg
ModelPart rightLeg = model.createPart("right_leg");
rightLeg.setPosition(20, 90);
Mesh2D rightLegMesh = Mesh2D.createQuad("right_leg_mesh", 20, 100);
rightLegMesh.setTexture(createSolidTexture(20, 100, 0xFF4169E1));
rightLeg.addMesh(rightLegMesh);
// 建立层级body 为根
body.addChild(head);
body.addChild(leftArm);
body.addChild(rightArm);
body.addChild(leftLeg);
body.addChild(rightLeg);
// 创建动画参数用于简单摆动(可选,示例中也直接对 Part 旋转)
model.createParameter("arm_swing", -1.0f, 1.0f, 0f);
model.createParameter("leg_swing", -1.0f, 1.0f, 0f);
model.createParameter("head_rotation", -0.5f, 0.5f, 0f);
System.out.println("Humanoid model created with parts: " + model.getParts().size());
}
private static void updateAnimation(Model2D model, float dt) {
animationTime += dt;
float armSwing = (float) Math.sin(animationTime * 3.0f) * 0.7f; // -0.7 .. 0.7
float legSwing = (float) Math.sin(animationTime * 3.0f + Math.PI) * 0.6f;
float headRot = (float) Math.sin(animationTime * 1.4f) * 0.15f;
model.setParameterValue("arm_swing", armSwing);
model.setParameterValue("leg_swing", legSwing);
model.setParameterValue("head_rotation", headRot);
ModelPart leftArm = model.getPart("left_arm");
ModelPart rightArm = model.getPart("right_arm");
ModelPart leftLeg = model.getPart("left_leg");
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);
// 更新物理与层级(如果 Model2D.update 会进行必要的矩阵/物理计算)
model.update(dt);
}
private static 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);
}
buf.flip();
Texture t = new Texture("solid_" + rgba + "_" + w + "x" + h, w, h, Texture.TextureFormat.RGBA, buf);
MemoryUtil.memFree(buf);
return t;
}
private static 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 * (1.0f - dist * 0.25f));
int g = (int) (200 * (1.0f - dist * 0.25f));
int b = (int) (180 * (1.0f - 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);
}
}