{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreib3fxd2cjrrg5twbuhmhikp76khiblq7cmqpylfzbcrjxocverp7i",
    "uri": "at://did:plc:dxjzgxe7cvirxkwfjr2tjspt/app.bsky.feed.post/3mgxa4txlslb2"
  },
  "path": "/t/an-attempt-at-vulkan/49433#post_2",
  "publishedAt": "2026-03-13T00:10:08.000Z",
  "site": "https://hub.jmonkeyengine.org",
  "tags": [
    "@Override"
  ],
  "textContent": "\n    package com.jme3.renderer.vulkan;\n\n    import com.jme3.scene.VertexBuffer;\n\n    import java.util.EnumMap;\n\n    /**\n     * VkMeshGpu:一个 jME3 Mesh 在 GPU(Vulkan) 侧对应的资源集合。\n     *\n     * 作用:\n     * - 把 jME3 的各个 VertexBuffer(Position/Color/Normal/UV...)映射成 Vulkan 的 VkBuffer。\n     * - 可选地保存 index buffer(索引缓冲),用于 vkCmdDrawIndexed。\n     *\n     * 说明:\n     * - 本类仅是“句柄/元数据容器”,不负责创建/销毁,生命周期通常由 VulkanRuntime/VkResourceFactory 管理。\n     * - 当前最小实现一般只用 Position + Color(与你的 pipeline/shader 输入匹配),后续可扩展更多语义。\n     */\n    public final class VkMeshGpu {\n\n        /**\n         * 顶点缓冲表:按 jME3 的 VertexBuffer.Type 索引到 Vulkan 的 VkBuffer。\n         *\n         * 例如:\n         * - Type.Position -> 一个包含 vec3(float) 的 Vulkan vertex buffer\n         * - Type.Color    -> 一个包含 vec3(float) 的 Vulkan vertex buffer\n         *\n         * 使用时:\n         * - 你的 VulkanPipeline 的 vertex input 需要与这些 buffer 的 stride/format 对齐\n         *   (比如 binding0=Position, binding1=Color)。\n         */\n        public final EnumMap<VertexBuffer.Type, VkBuffer> vbos =\n                new EnumMap<>(VertexBuffer.Type.class);\n\n        /**\n         * 索引缓冲(可选)。\n         *\n         * - 如果为 null:表示 mesh 没有索引缓冲,使用 vkCmdDraw(vertexCount, ...)\n         * - 如果非 null:表示 mesh 有索引缓冲,使用 vkCmdDrawIndexed(indexCount, ...)\n         */\n        public VkBuffer ibo;\n\n        /**\n         * index buffer 的索引数量(元素个数,不是字节数)。\n         * 仅在 ibo != null 时有效,用作 vkCmdDrawIndexed 的 indexCount 参数。\n         */\n        public int indexCount;\n\n        /**\n         * 顶点数量(vertex count)。\n         * - 对于非索引绘制:用作 vkCmdDraw 的 vertexCount 参数\n         * - 对于索引绘制:仍可用于校验/调试,但绘制主要用 indexCount\n         */\n        public int vertexCount;\n\n        /**\n         * Vulkan 的索引类型(vkCmdBindIndexBuffer 的 indexType 参数)。\n         *\n         * 常见取值:\n         * - VK_INDEX_TYPE_UINT16:对应 jME Format.UnsignedShort\n         * - VK_INDEX_TYPE_UINT32:对应 jME Format.UnsignedInt\n         * - VK_INDEX_TYPE_UINT8_EXT:对应 jME Format.UnsignedByte(需要 VK_EXT_index_type_uint8 扩展)\n         *\n         * 注意:\n         * - 如果 mesh 使用 UnsignedByte 但未启用扩展,这里可能无法正确绑定/绘制。\n         */\n        public int vkIndexType; // VK_INDEX_TYPE_UINT16 / VK_INDEX_TYPE_UINT32 / VK_INDEX_TYPE_UINT8_EXT\n    }\n\n\n\n\n    package com.jme3.lwjgl.test;\n\n    import com.jme3.renderer.Renderer;\n    import com.jme3.renderer.vulkan.LwjglVulkanContext;\n    import com.jme3.renderer.vulkan.VKRenderer;\n    import com.jme3.scene.Mesh;\n    import com.jme3.scene.VertexBuffer;\n    import com.jme3.system.AppSettings;\n    import com.jme3.system.SystemListener;\n    import com.jme3.util.BufferUtils;\n\n    import java.nio.FloatBuffer;\n\n    public class VulkanMainMeshTest implements SystemListener {\n\n        private LwjglVulkanContext context;\n\n        private Mesh triMesh;\n        private FloatBuffer posBuf;\n        private FloatBuffer colBuf;\n\n        private long lastTimeNs;\n        private float angleRad = 0f;\n\n        // 初始三角形(未旋转)\n    // 正方形(2 triangles = 6 verts)\n        private final float[] basePos = new float[]{\n            // tri 1\n            -0.6f, -0.6f, 0f,\n            0.6f, -0.6f, 0f,\n            0.6f, 0.6f, 0f,\n            // tri 2\n            0.6f, 0.6f, 0f,\n            -0.6f, 0.6f, 0f,\n            -0.6f, -0.6f, 0f\n        };\n\n        public static void main(String[] args) {\n            VulkanMainMeshTest app = new VulkanMainMeshTest();\n            app.start();\n        }\n\n        public void start() {\n            AppSettings settings = new AppSettings(true);\n            settings.setTitle(\"Vulkan Rotating Triangle\");\n            settings.setWidth(900);\n            settings.setHeight(700);\n            settings.setSamples(1);\n\n            context = new LwjglVulkanContext();\n            context.setSettings(settings);\n            context.setSystemListener(this);\n\n            context.create(false);\n        }\n\n        @Override\n        public void initialize() {\n            System.out.println(\"Vulkan System Initialized.\");\n\n            triMesh = new Mesh();\n            triMesh.setMode(Mesh.Mode.Triangles);\n\n            // 位置/颜色 buffer(3 verts * 3 comps)\n            posBuf = BufferUtils.createFloatBuffer(18);\n            colBuf = BufferUtils.createFloatBuffer(new float[]{\n                // tri 1 colors\n                1f, 0f, 0f,\n                0f, 1f, 0f,\n                0f, 0f, 1f,\n                // tri 2 colors\n                0f, 0f, 1f,\n                1f, 1f, 0f,\n                1f, 0f, 0f\n            });\n\n            // 写入初始位置\n            posBuf.put(basePos).rewind();\n            colBuf.rewind();\n\n            triMesh.setBuffer(VertexBuffer.Type.Position, 3, VertexBuffer.Format.Float, posBuf);\n            triMesh.setBuffer(VertexBuffer.Type.Color, 3, VertexBuffer.Format.Float, colBuf);\n\n            triMesh.updateBound();\n            triMesh.updateCounts();\n\n            lastTimeNs = System.nanoTime();\n        }\n\n        @Override\n        public void update() {\n            // 计算 dt\n            long now = System.nanoTime();\n            float dt = (now - lastTimeNs) / 1_000_000_000f;\n            lastTimeNs = now;\n\n            // 累积角度(1 rad/s,可自行调)\n            angleRad += dt;\n\n            // CPU 侧旋转三角形(绕 Z)\n            float c = (float) Math.cos(angleRad);\n            float s = (float) Math.sin(angleRad);\n\n            posBuf.rewind();\n            for (int i = 0; i < 6; i++) {\n                float x = basePos[i * 3];\n                float y = basePos[i * 3 + 1];\n                float z = basePos[i * 3 + 2];\n\n                float rx = x * c - y * s;\n                float ry = x * s + y * c;\n\n                posBuf.put(rx).put(ry).put(z);\n            }\n            posBuf.rewind();\n\n            // 提交给 Vulkan renderer\n            Renderer r = context.getRenderer();\n            if (r instanceof VKRenderer) {\n                ((VKRenderer) r).renderMesh(triMesh, 0, 1, null);\n            }\n        }\n\n        @Override\n        public void reshape(int width, int height) {\n        }\n\n        @Override\n        public void destroy() {\n        }\n\n        public void pause() {\n        }\n\n        public void resume() {\n        }\n\n        @Override\n        public void requestClose(boolean esc) {\n        }\n\n        @Override\n        public void gainFocus() {\n        }\n\n        @Override\n        public void loseFocus() {\n        }\n\n        @Override\n        public void handleError(String errorMsg, Throwable t) {\n            System.err.println(errorMsg);\n            if (t != null) {\n                t.printStackTrace();\n            }\n        }\n    }\n\n\n\nCurrently, I can use the mesh(class) to transfer vertices to Vulkan. However, I am unable to utilize functions like Geometry at the moment. I haven’t yet studied the material section. Once I am available next week, I will try to figure out a way to create a material backend so that the frontend of JME can directly use it (of course, this is an ideal situation and I’m not sure if it can be accomplished yet).",
  "title": "An attempt at Vulkan"
}