diff --git a/examples/image_display_demo/main.cpp b/examples/image_display_demo/main.cpp new file mode 100644 index 0000000..ff96804 --- /dev/null +++ b/examples/image_display_demo/main.cpp @@ -0,0 +1,98 @@ +#include +#include +#include + +using namespace extra2d; + +class ImageDisplayScene : public Scene { +public: + void onEnter() override { + Scene::onEnter(); + createCamera(); + createImageSprite(); + } + + void update(float dt) override { + Scene::update(dt); + if (textureLoaded_) { + return; + } + auto *rhiModule = RHIModule::get(); + if (!rhiModule || !rhiModule->getDevice()) { + return; + } + auto assets = getAssets(); + if (!assets || !spriteRenderer_) { + return; + } + auto texture = assets->load("assets/test.png"); + if (texture.isValid()) { + spriteRenderer_->setTexture(texture); + printf("图片加载成功: assets/test.png\n"); + } else { + printf("图片加载失败: assets/test.png\n"); + } + textureLoaded_ = true; + } + +private: + void createCamera() { + auto cameraNode = makePtr(); + cameraNode->setName("MainCamera"); + cameraNode->setPosition(0.0f, 0.0f); + + auto camera = makePtr(); + camera->setOrtho(0.0f, 1280.0f, 720.0f, 0.0f, -1.0f, 1.0f); + cameraNode->addComponent(camera); + setMainCamera(camera); + addChild(cameraNode); + } + + void createImageSprite() { + auto spriteNode = makePtr(); + spriteNode->setName("ImageSprite"); + spriteNode->setPosition(640.0f, 360.0f); + // spriteNode->setSize(384.0f, 384.0f); + spriteNode->setAnchor(0.5f, 0.5f); + + spriteRenderer_ = makePtr(); + // spriteRenderer_->setColor(Color::White); + spriteNode->addComponent(spriteRenderer_); + addChild(spriteNode); + } + + Ptr spriteRenderer_; + bool textureLoaded_ = false; +}; + +int main(int argc, char **argv) { + auto app = Application::create(); + + AppConfig config; + config.title = "Image Display Demo - Extra2D"; + config.width = 1280; + config.height = 720; + + if (!app->init(config)) { + printf("应用程序初始化失败!\n"); + return -1; + } + + SceneModule *sceneModule = app->getModule(); + if (!sceneModule) { + printf("获取场景模块失败!\n"); + return -1; + } + + Director *director = sceneModule->getDirector(); + if (!director) { + printf("获取导演器失败!\n"); + return -1; + } + + auto scene = makePtr(); + director->runScene(scene); + + app->run(); + return 0; +} diff --git a/examples/image_display_demo/romfs/assets/test.png b/examples/image_display_demo/romfs/assets/test.png new file mode 100644 index 0000000..3a2e8a5 Binary files /dev/null and b/examples/image_display_demo/romfs/assets/test.png differ diff --git a/examples/image_display_demo/romfs/shader/default.glsl b/examples/image_display_demo/romfs/shader/default.glsl new file mode 100644 index 0000000..8a2f9f5 --- /dev/null +++ b/examples/image_display_demo/romfs/shader/default.glsl @@ -0,0 +1,65 @@ +#type vertex +#version 320 es +precision highp float; + +layout(std140, binding = 0) uniform GlobalUBO { + mat4 uViewProjection; + vec4 uCameraPosition; + float uTime; + float uDeltaTime; + vec2 uScreenSize; +}; + +layout(std140, binding = 1) uniform MaterialUBO { + vec4 uColor; + vec4 uTintColor; + float uOpacity; + float uPadding[3]; +}; + +uniform mat4 uModelMatrix; + +layout(location = 0) in vec2 aPosition; +layout(location = 1) in vec2 aTexCoord; +layout(location = 2) in vec4 aColor; + +out vec2 vTexCoord; +out vec4 vColor; +out vec4 vTintColor; +out float vOpacity; + +void main() { + gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0); + vTexCoord = aTexCoord; + vColor = aColor * uColor; + vTintColor = uTintColor; + vOpacity = uOpacity; +} + +#type fragment +#version 320 es +precision highp float; + +in vec2 vTexCoord; +in vec4 vColor; +in vec4 vTintColor; +in float vOpacity; + +uniform sampler2D uTexture; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(uTexture, vTexCoord); + + if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) { + texColor = vec4(1.0, 1.0, 1.0, 1.0); + } + + fragColor = texColor * vColor * vTintColor; + fragColor.a *= vOpacity; + + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/examples/image_display_demo/xmake.lua b/examples/image_display_demo/xmake.lua new file mode 100644 index 0000000..2f182a6 --- /dev/null +++ b/examples/image_display_demo/xmake.lua @@ -0,0 +1,79 @@ +local example_dir = os.scriptdir() + +target("image_display_demo") + set_kind("binary") + add_files("main.cpp") + add_includedirs("../../include") + add_deps("extra2d") + + if is_plat("switch") then + set_targetdir("../../build/examples/image_display_demo") + + after_build(function (target) + local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro" + local elf_file = target:targetfile() + local output_dir = path.directory(elf_file) + local nacp_file = path.join(output_dir, "image_display_demo.nacp") + local nro_file = path.join(output_dir, "image_display_demo.nro") + local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe") + local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe") + local romfs = path.join(example_dir, "romfs") + local assets_source = path.join(example_dir, "../scene_graph_demo/romfs/assets") + local assets_target = path.join(romfs, "assets") + local shader_source = path.join(example_dir, "../../shader") + local shader_target = path.join(romfs, "shader") + + if not os.isdir(romfs) then + os.mkdir(romfs) + end + if not os.isdir(assets_target) then + os.mkdir(assets_target) + end + if os.isdir(assets_source) then + os.cp(path.join(assets_source, "**"), assets_target) + end + + if os.isdir(shader_source) then + if not os.isdir(shader_target) then + os.mkdir(shader_target) + end + os.cp(path.join(shader_source, "*"), shader_target) + end + + if os.isfile(nacptool) and os.isfile(elf2nro) then + os.vrunv(nacptool, {"--create", "Image Display Demo", "Extra2D Team", "1.0.0", nacp_file}) + if os.isdir(romfs) then + os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs}) + else + os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file}) + end + end + end) + elseif is_plat("mingw") then + set_targetdir("../../build/examples/image_display_demo") + + after_build(function (target) + local target_dir = path.directory(target:targetfile()) + local assets_dir = path.join(target_dir, "assets") + local assets_source = path.join(example_dir, "../scene_graph_demo/romfs/assets") + local shader_source = path.join(example_dir, "../../shader") + local shader_target = path.join(target_dir, "shader") + + if not os.isdir(assets_dir) then + os.mkdir(assets_dir) + end + if os.isdir(assets_source) then + os.cp(path.join(assets_source, "**"), assets_dir) + print("Copied assets from " .. assets_source .. " to " .. assets_dir) + end + + if os.isdir(shader_source) then + if not os.isdir(shader_target) then + os.mkdir(shader_target) + end + os.cp(path.join(shader_source, "*"), shader_target) + print("Copied shaders from " .. shader_source .. " to " .. shader_target) + end + end) + end +target_end() diff --git a/include/renderer/command_queue.h b/include/renderer/command_queue.h index f49881a..65ace2c 100644 --- a/include/renderer/command_queue.h +++ b/include/renderer/command_queue.h @@ -16,6 +16,7 @@ namespace extra2d { class CommandQueue; class Material; class Mesh; +class Texture; class UniformBufferManager; template class Ptr; @@ -295,6 +296,7 @@ public: * @param color 颜色 */ void submitDraw(Ptr material, Ptr mesh, + Ptr textureOverride, const struct Transform &transform, const Color &color, uint32_t sortKey = 0); diff --git a/include/renderer/render_types.h b/include/renderer/render_types.h index f4111da..f54898f 100644 --- a/include/renderer/render_types.h +++ b/include/renderer/render_types.h @@ -39,6 +39,7 @@ struct RenderCommand { struct DrawMeshData { Handle mesh; // 网格句柄 Handle material; // 材质句柄 + Handle texture; // 纹理句柄(用于默认材质覆盖) Vec2 pos; // 位置 Vec2 scale; // 缩放 float rot; // 旋转角度 @@ -69,6 +70,7 @@ struct RenderCommand { RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) { drawMesh.mesh = Handle::invalid(); drawMesh.material = Handle::invalid(); + drawMesh.texture = Handle::invalid(); drawMesh.pos = Vec2(0.0f, 0.0f); drawMesh.scale = Vec2(1.0f, 1.0f); drawMesh.rot = 0.0f; diff --git a/src/renderer/command_queue.cpp b/src/renderer/command_queue.cpp index db5c42f..5180fb7 100644 --- a/src/renderer/command_queue.cpp +++ b/src/renderer/command_queue.cpp @@ -360,6 +360,7 @@ UniformBuffer *CommandQueue::getCurrentMaterialUBO() const { } void CommandQueue::submitDraw(Ptr material, Ptr mesh, + Ptr textureOverride, const struct Transform &transform, const Color &color, uint32_t sortKey) { if (!material || !mesh || !material->getShader()) { @@ -391,12 +392,18 @@ void CommandQueue::submitDraw(Ptr material, Ptr mesh, // 设置纹理 const auto &textures = material->getTextures(); - cmd.textureCount = - static_cast(std::min(textures.size(), size_t(8))); - for (uint32_t i = 0; i < cmd.textureCount; ++i) { - if (textures[i].texture) { - cmd.textures[i] = textures[i].texture->getHandle(); + cmd.textureCount = 0; + for (const auto &textureSlot : textures) { + if (!textureSlot.texture) { + continue; } + uint32_t slot = std::min(textureSlot.slot, 7u); + cmd.textures[slot] = textureSlot.texture->getHandle(); + cmd.textureCount = std::max(cmd.textureCount, slot + 1); + } + if (textureOverride) { + cmd.textures[0] = textureOverride->getHandle(); + cmd.textureCount = std::max(cmd.textureCount, 1u); } // 分配材质 UBO 空间(使用 CPU 缓冲区) diff --git a/src/renderer/renderer_module.cpp b/src/renderer/renderer_module.cpp index 660749d..21a4272 100644 --- a/src/renderer/renderer_module.cpp +++ b/src/renderer/renderer_module.cpp @@ -199,11 +199,13 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) { defaultMeshPtr_ = mesh; } + Texture *texture = assets->get(cmd.drawMesh.texture); if (material && mesh) { Transform transform(cmd.drawMesh.pos, cmd.drawMesh.scale, cmd.drawMesh.rot); commandQueue_->submitDraw(Ptr(material), Ptr(mesh), - transform, cmd.drawMesh.color, cmd.sortKey); + Ptr(texture), transform, + cmd.drawMesh.color, cmd.sortKey); stats_.commandsSubmitted++; } else { E2D_WARN("提交绘制命令失败: 材质={}, 网格={}", material ? "有效" : "空", diff --git a/src/renderer/rhi/opengl/gl_pipeline.cpp b/src/renderer/rhi/opengl/gl_pipeline.cpp index 5d956b9..7c73fd2 100644 --- a/src/renderer/rhi/opengl/gl_pipeline.cpp +++ b/src/renderer/rhi/opengl/gl_pipeline.cpp @@ -61,6 +61,10 @@ void GLPipeline::bind() { // 绑定着色器程序 if (shaderProgram_ != 0) { glUseProgram(shaderProgram_); + GLint textureLocation = glGetUniformLocation(shaderProgram_, "uTexture"); + if (textureLocation != -1) { + glUniform1i(textureLocation, 0); + } } // 注意:VAO 需要与具体的顶点缓冲区绑定才能工作 diff --git a/src/renderer/texture.cpp b/src/renderer/texture.cpp index 241abb4..bc408d7 100644 --- a/src/renderer/texture.cpp +++ b/src/renderer/texture.cpp @@ -94,12 +94,7 @@ bool Texture::loadFromMemory(const uint8_t *data, int width, int height, // 上传纹理数据 if (data) { - // 注意:update 方法的参数需要根据实际 RHI 接口调整 - // 这里假设 update 接受数据指针 - texture->update( - data, static_cast(width * height * getBytesPerPixel(format))); - // 生成 mipmap - // 注意:generateMipmap 方法需要在 RHI 接口中实现 + texture->update(data, 0); } // 获取纹理句柄 @@ -227,8 +222,7 @@ bool Texture::reloadFromMemory(const uint8_t *data, int width, int height, // 上传纹理数据 if (data) { - texture->update( - data, static_cast(width * height * getBytesPerPixel(format))); + texture->update(data, 0); } // 替换旧的纹理句柄 diff --git a/src/scene/components/sprite_renderer.cpp b/src/scene/components/sprite_renderer.cpp index 48e6c87..3ec7f65 100644 --- a/src/scene/components/sprite_renderer.cpp +++ b/src/scene/components/sprite_renderer.cpp @@ -36,6 +36,7 @@ void SpriteRenderer::render() { // 如果没有指定网格,使用默认的四边形网格 cmd.drawMesh.mesh = Handle::invalid(); // RendererModule 会使用默认网格 cmd.drawMesh.material = material_.isValid() ? material_ : Handle::invalid(); + cmd.drawMesh.texture = texture_.isValid() ? texture_ : Handle::invalid(); cmd.setTransform(worldTransform); cmd.setColor(color_); diff --git a/xmake.lua b/xmake.lua index 829321b..06e1d23 100644 --- a/xmake.lua +++ b/xmake.lua @@ -95,6 +95,7 @@ define_extra2d_engine() if is_config("examples","true") then includes("examples/hello_world", {rootdir = "examples/hello_world"}) includes("examples/scene_graph_demo", {rootdir = "examples/scene_graph_demo"}) + includes("examples/image_display_demo", {rootdir = "examples/image_display_demo"}) end