feat(render): 实现模型渲染层级变换与网格世界坐标烘焙
- 为 Mesh2D 添加 getX/Y 方法并优化顶点访问逻辑 -修复 FloatBuffer 剩余空间判断逻辑 - 添加 bakedToWorld 标志支持网格世界坐标烘焙- 重构 ModelPart 变换更新逻辑,增加递归重计算 - 实现 ModelPart.draw() 方法支持 shader 传参绘制 - 更新 ModelRender 渲染流程,支持 worldTransform 传递 -修正网格顶点坐标上传逻辑,兼容 baked 状态- 移除废弃的调试与上传方法 - 增强部件变换时的局部与世界矩阵同步 - 修复 printWorldPosition 使用 worldTransform 坐标 - 调整测试模型初始位置与层级结构 重点: - 修复了XY轴无法设置的重大问题
This commit is contained in:
@@ -317,33 +317,58 @@ public final class ModelRender {
|
|||||||
checkGLError("render");
|
checkGLError("render");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关键修改点:在渲染前确保更新 part 的 worldTransform,
|
||||||
|
* 然后直接使用 part.getWorldTransform() 作为 uModelMatrix 传入 shader。
|
||||||
|
*/
|
||||||
private static void renderPartRecursive(ModelPart part, Matrix3f parentMat) {
|
private static void renderPartRecursive(ModelPart part, Matrix3f parentMat) {
|
||||||
Matrix3f local = part.getLocalTransform();
|
// 确保 part 的 local/world 矩阵被计算(会更新 transformDirty)
|
||||||
Matrix3f world = new Matrix3f(parentMat).mul(local); // world = parent * local
|
part.updateWorldTransform(parentMat, false);
|
||||||
|
|
||||||
// 从 world 矩阵取世界坐标
|
// 直接使用已经计算好的 worldTransform
|
||||||
//float worldX = world.m02;
|
Matrix3f world = part.getWorldTransform();
|
||||||
//float worldY = world.m12;
|
|
||||||
//System.out.println("Rendering part: " + part.getName() + " at world position: " + worldX + ", " + worldY);
|
// 先设置部件相关的 uniform(opacity / blendMode / color 等)
|
||||||
// 传入 shader
|
|
||||||
setUniformMatrix3(defaultProgram, "uModelMatrix", world);
|
|
||||||
setPartUniforms(defaultProgram, part);
|
setPartUniforms(defaultProgram, part);
|
||||||
|
|
||||||
|
// 把 world 矩阵传给 shader(兼容 uModelMatrix 和 可能的 uModel)
|
||||||
|
setUniformMatrix3(defaultProgram, "uModelMatrix", world);
|
||||||
|
setUniformMatrix3(defaultProgram, "uModel", world);
|
||||||
|
|
||||||
|
// 绘制本节点的所有 mesh(将 world 传入 renderMesh)
|
||||||
for (Mesh2D mesh : part.getMeshes()) {
|
for (Mesh2D mesh : part.getMeshes()) {
|
||||||
renderMesh(mesh);
|
renderMesh(mesh, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 递归渲染子节点,继续传入当前 world 作为子节点的 parent
|
||||||
for (ModelPart child : part.getChildren()) {
|
for (ModelPart child : part.getChildren()) {
|
||||||
renderPartRecursive(child, world);
|
renderPartRecursive(child, world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void renderMesh(Mesh2D mesh, Matrix3f modelMatrix) {
|
||||||
|
// 使用默认 shader(保证 shader 已被 use)
|
||||||
|
defaultProgram.use();
|
||||||
|
|
||||||
|
// 如果 mesh 已经被烘焙到世界坐标,则传 identity 矩阵给 shader(防止重复变换)
|
||||||
|
Matrix3f matToUse = mesh.isBakedToWorld() ? new Matrix3f().identity() : modelMatrix;
|
||||||
|
|
||||||
|
// 确保 shader 中的矩阵 uniform 已更新(再次设置以防遗漏)
|
||||||
|
setUniformMatrix3(defaultProgram, "uModelMatrix", matToUse);
|
||||||
|
setUniformMatrix3(defaultProgram, "uModel", matToUse);
|
||||||
|
|
||||||
|
// 调用 Mesh2D 的 draw 重载(传 program id 与实际矩阵)
|
||||||
|
try {
|
||||||
|
mesh.draw(defaultProgram.programId, matToUse);
|
||||||
|
} catch (AbstractMethodError | NoSuchMethodError e) {
|
||||||
|
// 回退:仍然兼容旧的无参 draw(在这种情况下 shader 的 uModelMatrix 已经被设置)
|
||||||
|
mesh.draw();
|
||||||
|
}
|
||||||
|
|
||||||
private static void renderMesh(Mesh2D mesh) {
|
|
||||||
mesh.draw();
|
|
||||||
checkGLError("renderMesh");
|
checkGLError("renderMesh");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int getGLDrawMode(int meshDrawMode) {
|
private static int getGLDrawMode(int meshDrawMode) {
|
||||||
switch (meshDrawMode) {
|
switch (meshDrawMode) {
|
||||||
case Mesh2D.POINTS: return GL11.GL_POINTS;
|
case Mesh2D.POINTS: return GL11.GL_POINTS;
|
||||||
@@ -357,78 +382,7 @@ public final class ModelRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ================== 上传数据 ==================(被弃用)
|
// ================== 上传数据 ==================(被弃用)
|
||||||
//private static void uploadMeshData(Mesh2D mesh, MeshGLResources res) {
|
|
||||||
// System.out.println("Uploading mesh data: " + mesh.getName());
|
|
||||||
//
|
|
||||||
// res.vao = GL30.glGenVertexArrays();
|
|
||||||
// GL30.glBindVertexArray(res.vao);
|
|
||||||
//
|
|
||||||
// res.vbo = GL15.glGenBuffers();
|
|
||||||
// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, res.vbo);
|
|
||||||
//
|
|
||||||
// float[] verts = mesh.getVertices();
|
|
||||||
// float[] uvs = mesh.getUVs();
|
|
||||||
// int vertexCount = mesh.getVertexCount();
|
|
||||||
// if (verts == null || verts.length == 0) throw new IllegalStateException("Mesh has no vertices: " + mesh.getName());
|
|
||||||
//
|
|
||||||
// FloatBuffer inter = MemoryUtil.memAllocFloat(vertexCount * 4);
|
|
||||||
//
|
|
||||||
// // ========== 添加调试输出 ==========
|
|
||||||
// System.out.println("=== Vertex data debug output ===");
|
|
||||||
// System.out.println("Grid name: " + mesh.getName());
|
|
||||||
// System.out.println("Number of vertices: " + vertexCount);
|
|
||||||
// System.out.println("Vertex coordinates (x, y):");
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < vertexCount; i++) {
|
|
||||||
// float x = verts[i*2];
|
|
||||||
// float y = verts[i*2+1];
|
|
||||||
// float u = uvs[i*2];
|
|
||||||
// float v = uvs[i*2+1];
|
|
||||||
//
|
|
||||||
// // 输出每个顶点的坐标和UV
|
|
||||||
// System.out.printf("vertex %d: location(%.3f, %.3f), UV(%.3f, %.3f)%n",
|
|
||||||
// i, x, y, u, v);
|
|
||||||
//
|
|
||||||
// inter.put(x);
|
|
||||||
// inter.put(y);
|
|
||||||
// inter.put(u);
|
|
||||||
// inter.put(v);
|
|
||||||
// }
|
|
||||||
// System.out.println("=== Vertex data output is over ===");
|
|
||||||
// // ========== 调试输出结束 ==========
|
|
||||||
//
|
|
||||||
// inter.flip();
|
|
||||||
// GL15.glBufferData(GL15.GL_ARRAY_BUFFER, inter, GL15.GL_STATIC_DRAW);
|
|
||||||
// MemoryUtil.memFree(inter);
|
|
||||||
//
|
|
||||||
// // 设置 attribute(位置 / uv),layout 已在 shader 中固定
|
|
||||||
// int stride = 4 * Float.BYTES;
|
|
||||||
// GL20.glEnableVertexAttribArray(0);
|
|
||||||
// GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, stride, 0);
|
|
||||||
// GL20.glEnableVertexAttribArray(1);
|
|
||||||
// GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, stride, 2 * Float.BYTES);
|
|
||||||
//
|
|
||||||
// int[] indices = mesh.getIndices();
|
|
||||||
// if (indices != null && indices.length > 0) {
|
|
||||||
// res.ebo = GL15.glGenBuffers();
|
|
||||||
// GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, res.ebo);
|
|
||||||
// IntBuffer ib = MemoryUtil.memAllocInt(indices.length);
|
|
||||||
// ib.put(indices).flip();
|
|
||||||
// GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, ib, GL15.GL_STATIC_DRAW);
|
|
||||||
// MemoryUtil.memFree(ib);
|
|
||||||
// res.vertexCount = indices.length; // drawElements 使用 count
|
|
||||||
// } else {
|
|
||||||
// res.vertexCount = vertexCount;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 不解绑 ELEMENT_ARRAY_BUFFER(它属于 VAO),解绑 ARRAY_BUFFER
|
|
||||||
// GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
|
||||||
// GL30.glBindVertexArray(0);
|
|
||||||
//
|
|
||||||
// res.initialized = true;
|
|
||||||
// checkGLError("uploadMeshData");
|
|
||||||
// System.out.println("Uploaded mesh: " + mesh.getName() + " (v=" + vertexCount + ")");
|
|
||||||
//}
|
|
||||||
|
|
||||||
// ================== uniform 设置辅助(内部使用,确保 program 已绑定) ==================
|
// ================== uniform 设置辅助(内部使用,确保 program 已绑定) ==================
|
||||||
private static void setUniformIntInternal(ShaderProgram sp, String name, int value) {
|
private static void setUniformIntInternal(ShaderProgram sp, String name, int value) {
|
||||||
@@ -480,11 +434,16 @@ public final class ModelRender {
|
|||||||
private static void setPartUniforms(ShaderProgram sp, ModelPart part) {
|
private static void setPartUniforms(ShaderProgram sp, ModelPart part) {
|
||||||
setUniformFloatInternal(sp, "uOpacity", part.getOpacity());
|
setUniformFloatInternal(sp, "uOpacity", part.getOpacity());
|
||||||
int blend = 0;
|
int blend = 0;
|
||||||
switch (part.getBlendMode()) {
|
ModelPart.BlendMode bm = part.getBlendMode();
|
||||||
case ADDITIVE: blend = 1; break;
|
if (bm != null) {
|
||||||
case MULTIPLY: blend = 2; break;
|
switch (bm) {
|
||||||
case SCREEN: blend = 3; break;
|
case ADDITIVE: blend = 1; break;
|
||||||
case NORMAL: default: blend = 0;
|
case MULTIPLY: blend = 2; break;
|
||||||
|
case SCREEN: blend = 3; break;
|
||||||
|
case NORMAL: default: blend = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blend = 0;
|
||||||
}
|
}
|
||||||
setUniformIntInternal(sp, "uBlendMode", blend);
|
setUniformIntInternal(sp, "uBlendMode", blend);
|
||||||
// 这里保留为白色,若需要部件 tint 请替换为 part 的 color 属性
|
// 这里保留为白色,若需要部件 tint 请替换为 part 的 color 属性
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ public class ModelPart {
|
|||||||
this.boundsDirty = true;
|
this.boundsDirty = true;
|
||||||
|
|
||||||
updateLocalTransform();
|
updateLocalTransform();
|
||||||
|
// 初始时 worldTransform = localTransform(无父节点时)
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 层级管理 ====================
|
// ==================== 层级管理 ====================
|
||||||
@@ -88,7 +90,9 @@ public class ModelPart {
|
|||||||
}
|
}
|
||||||
children.add(child);
|
children.add(child);
|
||||||
child.parent = this;
|
child.parent = this;
|
||||||
markTransformDirty();
|
child.markTransformDirty();
|
||||||
|
// 确保子节点的 worldTransform 立即更新
|
||||||
|
child.recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,7 +102,8 @@ public class ModelPart {
|
|||||||
boolean removed = children.remove(child);
|
boolean removed = children.remove(child);
|
||||||
if (removed) {
|
if (removed) {
|
||||||
child.parent = null;
|
child.parent = null;
|
||||||
markTransformDirty();
|
child.markTransformDirty();
|
||||||
|
child.recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
@@ -146,7 +151,7 @@ public class ModelPart {
|
|||||||
// ==================== 变换系统 ====================
|
// ==================== 变换系统 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新世界变换
|
* 更新世界变换(保留旧方法以兼容)
|
||||||
*/
|
*/
|
||||||
public void updateWorldTransform(Matrix3f parentTransform, boolean recursive) {
|
public void updateWorldTransform(Matrix3f parentTransform, boolean recursive) {
|
||||||
// 如果需要更新局部变换
|
// 如果需要更新局部变换
|
||||||
@@ -169,6 +174,21 @@ public class ModelPart {
|
|||||||
transformDirty = false;
|
transformDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void draw(int shaderProgram, org.joml.Matrix3f parentTransform) {
|
||||||
|
// 先确保 worldTransform 是最新的
|
||||||
|
updateWorldTransform(parentTransform, false);
|
||||||
|
|
||||||
|
// 绘制本节点的所有 mesh(将 worldTransform 作为 model 矩阵传入)
|
||||||
|
for (Mesh2D mesh : meshes) {
|
||||||
|
mesh.draw(shaderProgram, worldTransform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归绘制子节点
|
||||||
|
for (ModelPart child : children) {
|
||||||
|
child.draw(shaderProgram, worldTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新局部矩阵
|
// 更新局部矩阵
|
||||||
private void updateLocalTransform() {
|
private void updateLocalTransform() {
|
||||||
float cos = (float)Math.cos(rotation);
|
float cos = (float)Math.cos(rotation);
|
||||||
@@ -189,15 +209,36 @@ public class ModelPart {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 立即重新计算本节点的 worldTransform(并递归到子节点)
|
||||||
|
*/
|
||||||
|
private void recomputeWorldTransformRecursive() {
|
||||||
|
if (transformDirty) {
|
||||||
|
updateLocalTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
// parent.worldTransform 已经是最新(假设父节点正确维护)
|
||||||
|
parent.worldTransform.mul(localTransform, worldTransform);
|
||||||
|
} else {
|
||||||
|
worldTransform.set(localTransform);
|
||||||
|
}
|
||||||
|
|
||||||
// 打印世界坐标
|
// 递归更新子节点
|
||||||
public void printWorldPosition() {
|
for (ModelPart child : children) {
|
||||||
float worldX = localTransform.m02;
|
child.recomputeWorldTransformRecursive();
|
||||||
float worldY = localTransform.m12;
|
}
|
||||||
System.out.println("World position: " + worldX + ", " + worldY);
|
|
||||||
|
boundsDirty = true;
|
||||||
|
transformDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 打印世界坐标(修正为使用 worldTransform)
|
||||||
|
public void printWorldPosition() {
|
||||||
|
float worldX = worldTransform.m02();
|
||||||
|
float worldY = worldTransform.m12();
|
||||||
|
System.out.println("World position: " + worldX + ", " + worldY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标记变换需要更新
|
* 标记变换需要更新
|
||||||
@@ -215,11 +256,15 @@ public class ModelPart {
|
|||||||
public void setPosition(float x, float y) {
|
public void setPosition(float x, float y) {
|
||||||
position.set(x, y);
|
position.set(x, y);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(Vector2f position) {
|
public void setPosition(Vector2f position) {
|
||||||
this.position.set(position);
|
this.position.set(position);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -228,11 +273,15 @@ public class ModelPart {
|
|||||||
public void translate(float dx, float dy) {
|
public void translate(float dx, float dy) {
|
||||||
position.add(dx, dy);
|
position.add(dx, dy);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void translate(Vector2f delta) {
|
public void translate(Vector2f delta) {
|
||||||
position.add(delta);
|
position.add(delta);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,6 +290,8 @@ public class ModelPart {
|
|||||||
public void setRotation(float radians) {
|
public void setRotation(float radians) {
|
||||||
this.rotation = radians;
|
this.rotation = radians;
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -249,6 +300,8 @@ public class ModelPart {
|
|||||||
public void rotate(float deltaRadians) {
|
public void rotate(float deltaRadians) {
|
||||||
this.rotation += deltaRadians;
|
this.rotation += deltaRadians;
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,16 +310,22 @@ public class ModelPart {
|
|||||||
public void setScale(float sx, float sy) {
|
public void setScale(float sx, float sy) {
|
||||||
scale.set(sx, sy);
|
scale.set(sx, sy);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScale(float uniformScale) {
|
public void setScale(float uniformScale) {
|
||||||
scale.set(uniformScale, uniformScale);
|
scale.set(uniformScale, uniformScale);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScale(Vector2f scale) {
|
public void setScale(Vector2f scale) {
|
||||||
this.scale.set(scale);
|
this.scale.set(scale);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,6 +334,8 @@ public class ModelPart {
|
|||||||
public void scale(float sx, float sy) {
|
public void scale(float sx, float sy) {
|
||||||
scale.mul(sx, sy);
|
scale.mul(sx, sy);
|
||||||
markTransformDirty();
|
markTransformDirty();
|
||||||
|
updateLocalTransform();
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 网格管理 ====================
|
// ==================== 网格管理 ====================
|
||||||
@@ -283,6 +344,18 @@ public class ModelPart {
|
|||||||
* 添加网格
|
* 添加网格
|
||||||
*/
|
*/
|
||||||
public void addMesh(Mesh2D mesh) {
|
public void addMesh(Mesh2D mesh) {
|
||||||
|
if (mesh == null) return;
|
||||||
|
|
||||||
|
// 确保本节点的 worldTransform 是最新的(会递归更新子节点)
|
||||||
|
recomputeWorldTransformRecursive();
|
||||||
|
|
||||||
|
// 将 mesh 的每个顶点从本地空间变换到世界空间(烘焙)
|
||||||
|
int vc = mesh.getVertexCount();
|
||||||
|
for (int i = 0; i < vc; i++) {
|
||||||
|
org.joml.Vector2f local = mesh.getVertex(i);
|
||||||
|
org.joml.Vector2f worldPt = Matrix3fUtils.transformPoint(this.worldTransform, local);
|
||||||
|
mesh.setVertex(i, worldPt.x, worldPt.y);
|
||||||
|
}
|
||||||
meshes.add(mesh);
|
meshes.add(mesh);
|
||||||
boundsDirty = true;
|
boundsDirty = true;
|
||||||
}
|
}
|
||||||
@@ -517,4 +590,4 @@ public class ModelPart {
|
|||||||
", meshes=" + meshes.size() +
|
", meshes=" + meshes.size() +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import org.lwjgl.opengl.GL15;
|
|||||||
import org.lwjgl.opengl.GL20;
|
import org.lwjgl.opengl.GL20;
|
||||||
import org.lwjgl.opengl.GL30;
|
import org.lwjgl.opengl.GL30;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
import java.nio.FloatBuffer;
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
/**
|
/**
|
||||||
* 2D网格类,用于存储和管理2D模型的几何数据
|
* 2D网格类,用于存储和管理2D模型的几何数据
|
||||||
* 支持顶点、UV坐标、索引和变形操作
|
* 支持顶点、UV坐标、索引和变形操作
|
||||||
@@ -40,6 +39,7 @@ public class Mesh2D {
|
|||||||
private boolean dirty = true; // 数据是否已修改
|
private boolean dirty = true; // 数据是否已修改
|
||||||
private BoundingBox bounds;
|
private BoundingBox bounds;
|
||||||
private boolean boundsDirty = true;
|
private boolean boundsDirty = true;
|
||||||
|
private boolean bakedToWorld = false;
|
||||||
|
|
||||||
// ==================== 常量 ====================
|
// ==================== 常量 ====================
|
||||||
public static final int POINTS = 0;
|
public static final int POINTS = 0;
|
||||||
@@ -178,8 +178,7 @@ public class Mesh2D {
|
|||||||
if (index < 0 || index >= getVertexCount()) {
|
if (index < 0 || index >= getVertexCount()) {
|
||||||
throw new IndexOutOfBoundsException("Vertex index out of bounds: " + index);
|
throw new IndexOutOfBoundsException("Vertex index out of bounds: " + index);
|
||||||
}
|
}
|
||||||
int baseIndex = index * 2;
|
return dest.set(getX(index), getY(index));
|
||||||
return dest.set(vertices[baseIndex], vertices[baseIndex + 1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2f getVertex(int index) {
|
public Vector2f getVertex(int index) {
|
||||||
@@ -308,7 +307,7 @@ public class Mesh2D {
|
|||||||
* 获取顶点缓冲区数据
|
* 获取顶点缓冲区数据
|
||||||
*/
|
*/
|
||||||
public FloatBuffer getVertexBuffer(FloatBuffer buffer) {
|
public FloatBuffer getVertexBuffer(FloatBuffer buffer) {
|
||||||
if (buffer == null || buffer.capacity() < vertices.length) {
|
if (buffer == null || buffer.remaining() < vertices.length) {
|
||||||
throw new IllegalArgumentException("Buffer is null or too small");
|
throw new IllegalArgumentException("Buffer is null or too small");
|
||||||
}
|
}
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
@@ -330,6 +329,20 @@ public class Mesh2D {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getX(int index) {
|
||||||
|
if (index < 0 || index >= getVertexCount()) {
|
||||||
|
throw new IndexOutOfBoundsException("Vertex index out of bounds: " + index);
|
||||||
|
}
|
||||||
|
return vertices[index * 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY(int index) {
|
||||||
|
if (index < 0 || index >= getVertexCount()) {
|
||||||
|
throw new IndexOutOfBoundsException("Vertex index out of bounds: " + index);
|
||||||
|
}
|
||||||
|
return vertices[index * 2 + 1];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取索引缓冲区数据
|
* 获取索引缓冲区数据
|
||||||
*/
|
*/
|
||||||
@@ -350,17 +363,18 @@ public class Mesh2D {
|
|||||||
int vertexCount = getVertexCount();
|
int vertexCount = getVertexCount();
|
||||||
int floatCount = vertexCount * 4; // 每个顶点:x, y, u, v
|
int floatCount = vertexCount * 4; // 每个顶点:x, y, u, v
|
||||||
|
|
||||||
if (buffer == null || buffer.capacity() < floatCount) {
|
if (buffer == null || buffer.remaining() < floatCount) {
|
||||||
throw new IllegalArgumentException("Buffer is null or too small");
|
throw new IllegalArgumentException("Buffer is null or too small");
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
for (int i = 0; i < vertexCount; i++) {
|
for (int i = 0; i < vertexCount; i++) {
|
||||||
System.out.println("x:" + vertices[i * 2] + "y:" + vertices[i * 2 + 1]);
|
// 明确使用定位方法,避免下标算错
|
||||||
buffer.put(vertices[i * 2]); // x
|
//System.out.println("x:"+ getX(i) + "y:"+ getY(i));
|
||||||
buffer.put(vertices[i * 2 + 1]); // y
|
buffer.put(getX(i)); // x
|
||||||
buffer.put(uvs[i * 2]); // u
|
buffer.put(getY(i)); // y
|
||||||
buffer.put(uvs[i * 2 + 1]); // v
|
buffer.put(uvs[i * 2]); // u
|
||||||
|
buffer.put(uvs[i * 2 + 1]); // v
|
||||||
}
|
}
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
return buffer;
|
return buffer;
|
||||||
@@ -446,7 +460,7 @@ public class Mesh2D {
|
|||||||
/**
|
/**
|
||||||
* 绘制网格(会在第一次绘制时自动上传到 GPU)
|
* 绘制网格(会在第一次绘制时自动上传到 GPU)
|
||||||
*/
|
*/
|
||||||
public void draw() {
|
public void draw(int shaderProgram, org.joml.Matrix3f modelMatrix) {
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
if (indices == null || indices.length == 0) return;
|
if (indices == null || indices.length == 0) return;
|
||||||
|
|
||||||
@@ -458,10 +472,46 @@ public class Mesh2D {
|
|||||||
texture.bind();
|
texture.bind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 绑定 VAO
|
||||||
|
GL30.glBindVertexArray(vaoId);
|
||||||
|
|
||||||
|
// 将 modelMatrix 上传到 shader 的 uniform "uModel"(如果 shader 有此 uniform)
|
||||||
|
int loc = GL20.glGetUniformLocation(shaderProgram, "uModel");
|
||||||
|
if (loc != -1) {
|
||||||
|
// 用一个 FloatBuffer 传递 3x3 矩阵
|
||||||
|
java.nio.FloatBuffer fb = org.lwjgl.system.MemoryUtil.memAllocFloat(9);
|
||||||
|
try {
|
||||||
|
modelMatrix.get(fb); // JOML 将矩阵写入 buffer(列主序,适合 OpenGL)
|
||||||
|
fb.flip();
|
||||||
|
GL20.glUniformMatrix3fv(loc, false, fb);
|
||||||
|
} finally {
|
||||||
|
org.lwjgl.system.MemoryUtil.memFree(fb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制
|
||||||
|
GL11.glDrawElements(GL11.GL_TRIANGLES, indexCount, GL11.GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
|
// 解绑
|
||||||
|
GL30.glBindVertexArray(0);
|
||||||
|
|
||||||
|
if (texture != null) {
|
||||||
|
texture.unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw() {
|
||||||
|
if (!visible) return;
|
||||||
|
if (indices == null || indices.length == 0) return;
|
||||||
|
if (!uploaded) {
|
||||||
|
uploadToGPU();
|
||||||
|
}
|
||||||
|
if (texture != null) {
|
||||||
|
texture.bind();
|
||||||
|
}
|
||||||
GL30.glBindVertexArray(vaoId);
|
GL30.glBindVertexArray(vaoId);
|
||||||
GL11.glDrawElements(GL11.GL_TRIANGLES, indexCount, GL11.GL_UNSIGNED_INT, 0);
|
GL11.glDrawElements(GL11.GL_TRIANGLES, indexCount, GL11.GL_UNSIGNED_INT, 0);
|
||||||
GL30.glBindVertexArray(0);
|
GL30.glBindVertexArray(0);
|
||||||
|
|
||||||
if (texture != null) {
|
if (texture != null) {
|
||||||
texture.unbind();
|
texture.unbind();
|
||||||
}
|
}
|
||||||
@@ -582,6 +632,15 @@ public class Mesh2D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 标记或查询网格顶点是否已经被烘焙到世界坐标 */
|
||||||
|
public void setBakedToWorld(boolean baked) {
|
||||||
|
this.bakedToWorld = baked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBakedToWorld() {
|
||||||
|
return bakedToWorld;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Object 方法 ====================
|
// ==================== Object 方法 ====================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -617,4 +676,4 @@ public class Mesh2D {
|
|||||||
", bounds=" + getBounds() +
|
", bounds=" + getBounds() +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class ModelRenderTest {
|
|||||||
|
|
||||||
// body 放在屏幕中心
|
// body 放在屏幕中心
|
||||||
ModelPart body = testModel.createPart("body");
|
ModelPart body = testModel.createPart("body");
|
||||||
body.setPosition(400, 320);
|
body.setPosition(0, 0);
|
||||||
// 身体网格:宽 80 高 120
|
// 身体网格:宽 80 高 120
|
||||||
Mesh2D bodyMesh = Mesh2D.createQuad("body_mesh", 80, 120);
|
Mesh2D bodyMesh = Mesh2D.createQuad("body_mesh", 80, 120);
|
||||||
bodyMesh.setTexture(createSolidTexture(64, 128, 0xFF4A6AFF)); // 蓝衣
|
bodyMesh.setTexture(createSolidTexture(64, 128, 0xFF4A6AFF)); // 蓝衣
|
||||||
@@ -151,6 +151,7 @@ public class ModelRenderTest {
|
|||||||
|
|
||||||
// 建立层级:body 为根,其他作为 body 的子节点
|
// 建立层级:body 为根,其他作为 body 的子节点
|
||||||
//testModel.addPart(body);
|
//testModel.addPart(body);
|
||||||
|
|
||||||
body.addChild(head);
|
body.addChild(head);
|
||||||
body.addChild(leftArm);
|
body.addChild(leftArm);
|
||||||
body.addChild(rightArm);
|
body.addChild(rightArm);
|
||||||
|
|||||||
Reference in New Issue
Block a user