2025-10-13 10:56:56 +08:00
|
|
|
|
package com.chuangzhou.vivid2D.test;
|
|
|
|
|
|
|
|
|
|
|
|
import com.chuangzhou.vivid2D.render.ModelGLPanel;
|
|
|
|
|
|
import com.chuangzhou.vivid2D.render.model.Model2D;
|
2025-10-13 22:12:30 +08:00
|
|
|
|
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;
|
2025-10-13 10:56:56 +08:00
|
|
|
|
|
|
|
|
|
|
import javax.swing.*;
|
2025-10-13 22:12:30 +08:00
|
|
|
|
import java.awt.event.ActionEvent;
|
|
|
|
|
|
import java.nio.ByteBuffer;
|
2025-10-13 10:56:56 +08:00
|
|
|
|
|
2025-10-13 22:12:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 在原 TestModelGLPanel 的基础上增加简单动画(手臂、腿、头部摆动)
|
|
|
|
|
|
* @author tzdwindows 7
|
|
|
|
|
|
*/
|
2025-10-13 10:56:56 +08:00
|
|
|
|
public class TestModelGLPanel {
|
|
|
|
|
|
|
|
|
|
|
|
private static final String MODEL_PATH = "C:\\Users\\Administrator\\Desktop\\trump_texture.model";
|
2025-10-13 22:12:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 使 testModel 与动画计时可访问
|
|
|
|
|
|
private static Model2D testModel;
|
|
|
|
|
|
private static float animationTime = 0f;
|
|
|
|
|
|
private static boolean animate = true;
|
|
|
|
|
|
|
2025-10-13 10:56:56 +08:00
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
SwingUtilities.invokeLater(() -> {
|
|
|
|
|
|
JFrame frame = new JFrame("ModelGLPanel Demo");
|
|
|
|
|
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
2025-10-13 22:12:30 +08:00
|
|
|
|
|
2025-10-13 10:56:56 +08:00
|
|
|
|
ModelGLPanel glPanel = null;
|
|
|
|
|
|
try {
|
2025-10-13 22:12:30 +08:00
|
|
|
|
// 先创建一个空的 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"));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-10-13 10:56:56 +08:00
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
}
|
2025-10-13 22:12:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 将 GL 面板加入窗体并显示
|
2025-10-13 10:56:56 +08:00
|
|
|
|
frame.add(glPanel);
|
|
|
|
|
|
frame.pack();
|
|
|
|
|
|
frame.setLocationRelativeTo(null);
|
|
|
|
|
|
frame.setVisible(true);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-10-13 22:12:30 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
2025-10-13 10:56:56 +08:00
|
|
|
|
}
|