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); } }