diff --git a/Vivid2DRenderer/AlignedMemoryStack.cpp b/Vivid2DRenderer/AlignedMemoryStack.cpp new file mode 100644 index 0000000..6d9865e --- /dev/null +++ b/Vivid2DRenderer/AlignedMemoryStack.cpp @@ -0,0 +1,2 @@ +#include "pch.h" +#include "AlignedMemoryStack.h" diff --git a/Vivid2DRenderer/AlignedMemoryStack.h b/Vivid2DRenderer/AlignedMemoryStack.h new file mode 100644 index 0000000..ee3871c --- /dev/null +++ b/Vivid2DRenderer/AlignedMemoryStack.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include + +class AlignedMemoryStack { + char* buffer = nullptr; + size_t capacity = 0; + size_t offset = 0; + std::stack markers; + +public: + AlignedMemoryStack(size_t initialSize = 64 * 1024) { + buffer = new char[initialSize]; + capacity = initialSize; + } + + ~AlignedMemoryStack() { + delete[] buffer; + } + + void* pushStackAlloc(size_t size, size_t align = alignof(std::max_align_t)) { + size_t aligned = (offset + align - 1) & ~(align - 1); + if (aligned + size > capacity) { + // 简单扩容策略:直接翻倍 + size_t newCapacity = (aligned + size) * 2; + char* newBuffer = new char[newCapacity]; + std::memcpy(newBuffer, buffer, offset); + delete[] buffer; + buffer = newBuffer; + capacity = newCapacity; + } + void* ptr = buffer + aligned; + markers.push(offset); + offset = aligned + size; + return ptr; + } + + template + PtrType* pushStackAllocate(size_t size, size_t align = alignof(std::max_align_t)) { + return pushStackAlloc(size, align); + } + + void popStack() { + offset = markers.top(); + markers.pop(); + } + + void reset() { + offset = 0; + markers = std::stack(); + } +}; \ No newline at end of file diff --git a/Vivid2DRenderer/ModelRender.cpp b/Vivid2DRenderer/ModelRender.cpp new file mode 100644 index 0000000..bc6632e --- /dev/null +++ b/Vivid2DRenderer/ModelRender.cpp @@ -0,0 +1,1048 @@ +#include "pch.h" +#include "ModelRender.h" + + +ModelRender* ModelRender::s_instance = nullptr; + +ModelRender::Matrix3f ModelRender::buildCameraProjection(int width, int height) +{ + Matrix3f m; + + if (m_camera.isEnabled()) { + // 考虑摄像机缩放和平移 + GLfloat zoom = m_camera.getZoom(); + Vector2f pos = m_camera.getPosition(); + m = Matrix3f( + 2.0f * zoom / width, 0.0f, -1.0f - (2.0f * zoom * pos.x / width), + 0.0f, -2.0f * zoom / height, 1.0f + (2.0f * zoom * pos.y / height), + 0.0f, 0.0f, 1.0f + ); + } + else { + // 原始投影矩阵 + m = Matrix3f( + 2.0f / width, 0.0f, -1.0f, + 0.0f, -2.0f / height, 1.0f, + 0.0f, 0.0f, 1.0f + ); + } + + return m; +} + +void ModelRender::initialize() noexcept(false) +{ + if (m_isInitialized) return; + + logger.info("Initializing ModelRender..."); + + // 初始化渲染系统 + RenderSystem::beginInitialization(); + RenderSystem::initRenderThread(); + + logGLInfo(); + setupGLState(); + + try { + compileDefaultShader(); + + // 初始化所有非默认着色器的基础信息 + initNonDefaultShaders(); + + } + catch (std::runtime_error ex) { + logger.error("Failed to compile default shader: {}", ex.what()); + throw ex; + } + + + createDefaultTexture(); + RenderSystem::viewport(0, 0, m_viewportSize.width, m_viewportSize.height); + RenderSystem::finishInitialization(); + + try { + // TODO: 替换为字体 + + // 初始化默认字体(可替换为你自己的 TTF 数据) + ByteBuffer fontData; + try { + fontData = RenderSystem::loadFont("FZYTK.TTF"); + } + catch (std::exception e) { + logger.warning("Failed to load Arial.ttf, trying fallback fonts", e); + // 尝试其他字体 + try { + fontData = RenderSystem::loadFont("arial.ttf"); + } + catch (std::exception e2) { + try { + fontData = RenderSystem::loadFont("times.ttf"); + } + catch (std::exception e3) { + logger.error("All font loading attempts failed"); + } + } + } + + if (!fontData.empty()) { + m_defaultTextRenderer = std::make_unique(m_fontBitmapSize.width, m_fontBitmapSize.height, m_fontFirstChar, m_fontCharCount); + RenderSystem::checkGLError("TextRenderer constructor"); + + m_defaultTextRenderer->initialize(fontData, 20.0f); + RenderSystem::checkGLError("m_defaultTextRenderer initialization"); + + if (!m_defaultTextRenderer->isInitialized()) { + logger.error("TextRenderer failed to initialize properly"); + } + } + else { + logger.error("No valid font data available for text rendering"); + } + } + catch (std::exception e) { + logger.warning("Failed to initialize default text renderer", e); + } + + m_isInitialized = true; + logger.info("ModelRender m_isInitialized successfully"); +} + +void ModelRender::initNonDefaultShaders() +{ + std::vector> shaderList = ShaderManagement::s_shaderList; + if (shaderList.empty()) { + logger.info("No shaders found to initialize"); + return; + } + + int nonDefaultCount = 0; + for (auto& shader : shaderList) { + // 跳过默认着色器,只初始化非默认的 + if (shader->isDefaultShader()) { + continue; + } + + try { + // 获取着色器程序 + ShaderProgram* program = ShaderManagement::getShaderProgram(shader->getShaderName()); + if (program == nullptr) { + logger.warning("Shader program not found for: {}", shader->getShaderName()); + continue; + } + + // 设置着色器的基础uniforms(主要是顶点坐标相关的) + initShaderBasicUniforms(program, shader.get()); + nonDefaultCount++; + + logger.debug("Initialized non-default shader: {}", shader->getShaderName()); + + } + catch (std::exception e) { + logger.error("Failed to initialize non-default shader: {}", shader->getShaderName(), e); + } + } + + logger.info("Initialized {} non-default shaders", nonDefaultCount); +} + +void ModelRender::initShaderBasicUniforms(ShaderProgram* program, CompleteShader* shader) +{ + program->use(); + + try { + // 设置基础的变换矩阵为单位矩阵 + setUniformMatrix3(program, "uModelMatrix", Matrix3f(1.0f)); + setUniformMatrix3(program, "uViewMatrix", Matrix3f(1.0f)); + + // 设置投影矩阵(使用当前视口尺寸) + Matrix3f projection = buildOrthoProjection(m_viewportSize.width, m_viewportSize.height); + setUniformMatrix3(program, "uProjectionMatrix", projection); + + // 设置基础颜色为白色 + setUniformVec4Internal(program, "uColor", Vector4f(1.0f, 1.0f, 1.0f, 1.0f)); + + // 设置基础不透明度 + setUniformFloatInternal(program, "uOpacity", 1.0f); + + // 设置纹理单元(如果有纹理的话) + setUniformIntInternal(program, "uTexture", 0); + + RenderSystem::checkGLError("initShaderBasicUniforms_" + shader->getShaderName()); + + } + catch (...) { + program->stop(); + throw; + } +} + +void ModelRender::logGLInfo() +{ + RenderSystem::logDetailedGLInfo(); +} + +void ModelRender::uploadLightsToShader(ShaderProgram* sp, Model2D& model) +{ + std::vector lights = model.getLights(); + int idx = 0; + + for (int i = 0; i < lights.size() && idx < m_maxLightCount; i++) { + LightSource l = lights[i]; + if (!l.isEnabled()) continue; + std::string idxStr = std::to_string(idx); + + // 基础属性 + setUniformVec2Internal(sp, "uLightsPos[" + idxStr + "]", l.isAmbient() ? Vector2f(0.0f, 0.0f) : l.getPosition()); + setUniformVec3Internal(sp, "uLightsColor[" + idxStr + "]", l.getColor()); + setUniformFloatInternal(sp, "uLightsIntensity[" + idxStr + "]", l.getIntensity()); + setUniformIntInternal(sp, "uLightsIsAmbient[" + idxStr + "]", l.isAmbient() ? 1 : 0); + + // 辉光相关(如果没有被设置也安全地上传默认值) + //setUniformIntInternal(sp, "uLightsIsGlow[" + idxStr + "]", l.isGlow() ? 1 : 0); + //setUniformVec2Internal(sp, "uLightsGlowDir[" + idxStr + "]", l.getGlowDirection() ? l.getGlowDirection() : Vector2f(0.0f, 0.0f)); + //setUniformFloatInternal(sp, "uLightsGlowIntensity[" + idxStr + "]", l.getGlowIntensity()); + //setUniformFloatInternal(sp, "uLightsGlowRadius[" + idxStr + "]", l.getGlowRadius()); + //setUniformFloatInternal(sp, "uLightsGlowAmount[" + idxStr + "]", l.getGlowAmount()); + + idx++; + } + + // 上传实际有效光源数量 + setUniformIntInternal(sp, "uLightCount", idx); + + // 禁用剩余槽位(确保 shader 中不会读取到垃圾值) + for (int i = idx; i < m_maxLightCount; i++) { + std::string idxStr = std::to_string(i); + setUniformFloatInternal(sp, "uLightsIntensity[" + idxStr + "]", 0.0f); + setUniformIntInternal(sp, "uLightsIsAmbient[" + idxStr + "]", 0); + setUniformVec3Internal(sp, "uLightsColor[" + idxStr + "]", Vector3f(0.0f, 0.0f, 0.0f)); + setUniformVec2Internal(sp, "uLightsPos[" + idxStr + "]", Vector2f(0.0f, 0.0f)); + + // 关闭辉光槽 + setUniformIntInternal(sp, "uLightsIsGlow[" + idxStr + "]", 0); + setUniformVec2Internal(sp, "uLightsGlowDir[" + idxStr + "]", Vector2f(0.0f, 0.0f)); + setUniformFloatInternal(sp, "uLightsGlowIntensity[" + idxStr + "]", 0.0f); + setUniformFloatInternal(sp, "uLightsGlowRadius[" + idxStr + "]", 0.0f); + setUniformFloatInternal(sp, "uLightsGlowAmount[" + idxStr + "]", 0.0f); + } +} + +void ModelRender::setupGLState() +{ + RenderSystem::checkGLError("setupGLState_start"); + + RenderSystem::clearColor(m_clearColor.x, m_clearColor.y, m_clearColor.z, m_clearColor.w); + RenderSystem::checkGLError("after_clearColor"); + + if (m_enableBlending) { + RenderSystem::enableBlend(); + RenderSystem::checkGLError("after_enableBlend"); + + RenderSystem::blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + RenderSystem::checkGLError("after_blendFunc"); + } + else { + RenderSystem::disableBlend(); + RenderSystem::checkGLError("after_disableBlend"); + } + + if (m_enableDepthTest) { + RenderSystem::enableDepthTest(); + RenderSystem::checkGLError("after_enableDepthTest"); + + RenderSystem::depthFunc(GL_LEQUAL); + RenderSystem::checkGLError("after_depthFunc"); + + RenderSystem::depthMask(true); + RenderSystem::checkGLError("after_depthMask"); + + RenderSystem::clearDepth(1.0); + RenderSystem::checkGLError("after_clearDepth"); + } + else { + RenderSystem::disableDepthTest(); + RenderSystem::checkGLError("after_disableDepthTest"); + } + + RenderSystem::checkGLError("after_disableCullFace"); +} + +void ModelRender::compileDefaultShader() +{ + ShaderManagement::compileAllShaders(); + m_defaultProgram = ShaderManagement::getDefaultProgram(); + if (m_defaultProgram == nullptr) { + throw std::runtime_error("Failed to compile default shader: no default shader found"); + } +} + +void ModelRender::createDefaultTexture() +{ + RenderSystem::assertOnRenderThread(); + m_defaultTextureId = RenderSystem::createDefaultTexture(); + RenderSystem::checkGLError("createDefaultTexture"); +} + +void ModelRender::cleanup() +{ + if (!m_isInitialized) return; + + logger.info("Cleaning up ModelRender..."); + + // mesh resources + for (auto& [k, r] : m_meshResources) r.dispose(); + m_meshResources.clear(); + + // 使用新的着色器管理系统清理着色器 + ShaderManagement::cleanup(); + m_defaultProgram = nullptr; + + // textures + if (m_defaultTextureId != 0) { + RenderSystem::deleteTextures(m_defaultTextureId); + m_defaultTextureId = 0; + } + + m_isInitialized = false; + logger.info("ModelRender cleaned up"); +} + +void ModelRender::render(GLfloat deltaTime, Model2D& model) +{ + if (!m_isInitialized) throw IllegalStateException("ModelRender not m_isInitialized"); + + // 确保在渲染线程 + RenderSystem::assertOnRenderThread(); + + // 添加前置错误检查 + RenderSystem::checkGLError("render_start"); + + // 物理系统更新 + PhysicsSystem* physics = model.getPhysics(); + if (physics && physics->isEnabled()) { + physics->update(deltaTime, model); + } + + model.update(deltaTime); + + // 检查清除操作前的状态 + RenderSystem::checkGLError("before_clear"); + RenderSystem::clear(GL_COLOR_BUFFER_BIT | (m_enableDepthTest ? GL_DEPTH_BUFFER_BIT : 0)); + RenderSystem::checkGLError("after_clear"); + + // 检查着色器程序 + if (!m_defaultProgram || m_defaultProgram->getProgramId() == 0) { + logger.error("Default shader program is not m_isInitialized"); + return; + } + + // 设置投影与视图矩阵(使用摄像机变换) + Matrix3f proj = buildCameraProjection(m_viewportSize.width, m_viewportSize.height); + Matrix3f view(1.0f); + + // 1. 首先设置默认着色器 + m_defaultProgram->use(); + RenderSystem::checkGLError("after_use_default_program"); + + // 设置默认着色器的投影与视图 + setUniformMatrix3(m_defaultProgram, "uProjectionMatrix", proj); + setUniformMatrix3(m_defaultProgram, "uViewMatrix", view); + RenderSystem::checkGLError("after_set_default_matrices"); + + // 设置摄像机Z轴位置(如果着色器支持) + setUniformFloatInternal(m_defaultProgram, "uCameraZ", m_camera.getZPosition()); + RenderSystem::checkGLError("after_set_camera_z"); + + // 添加光源数据上传到默认着色器 + uploadLightsToShader(m_defaultProgram, model); + RenderSystem::checkGLError("after_upload_lights"); + + // 2. 设置非默认着色器的顶点坐标相关uniform + setupNonDefaultShaders(proj, view); + RenderSystem::checkGLError("after_setup_non_default_shaders"); + + // 在渲染光源位置前检查 + RenderSystem::checkGLError("before_render_light_positions"); + renderLightPositions(model); + RenderSystem::checkGLError("after_render_light_positions"); + + // 递归渲染所有根部件(使用默认着色器) + Matrix3f identity = Matrix3f(1.0f); + for (auto& p : model.getParts()) { + if (p->getParent()) continue; + renderPartRecursive(p, identity); + } + RenderSystem::checkGLError("after_render_parts"); + + if (m_renderColliders && physics) { + renderPhysicsColliders(physics); + RenderSystem::checkGLError("after_render_colliders"); + } + + //if (m_defaultTextRenderer) { + // std::string camInfo = std::string.format("Camera X: %.2f Y: %.2f Zoom: %.2f", + // m_camera.getPosition().x, + // m_camera.getPosition().y, + // m_camera.getZoom()); + // GLfloat x = 10.0f; + // GLfloat y = m_viewportSize.height - 30.0f; + // Vector4f color = new Vector4f(1.0f, 1.0f, 1.0f, 1.0f); + // renderText(camInfo, x, y, color); + // RenderSystem::checkGLError("renderText"); + //} + + RenderSystem::checkGLError("render_end"); +} + + +void ModelRender::renderThumbnail(Model2D& model, GLfloat x, GLfloat y, GLfloat width, GLfloat height) +{ + if (!m_isInitialized) throw IllegalStateException("ModelRender not m_isInitialized"); + + RenderSystem::assertOnRenderThread(); + RenderSystem::checkGLError("renderThumbnail_start"); + + // 保存原始状态以便恢复 + bool originalRenderColliders = m_renderColliders; + bool originalRenderLightPositions = m_renderLightPositions; + int originalViewportWidth = m_viewportSize.width; + int originalViewportHeight = m_viewportSize.height; + + try { + // 设置缩略图专用状态 + m_renderColliders = false; + m_renderLightPositions = false; + + // 设置缩略图视口(屏幕坐标) + RenderSystem::viewport((int)x, (int)y, (int)width, (int)height); + + // 清除缩略图区域 + RenderSystem::clear(GL_COLOR_BUFFER_BIT | (m_enableDepthTest ? GL_DEPTH_BUFFER_BIT : 0)); + RenderSystem::checkGLError("thumbnail_after_clear"); + + // 简化版的模型更新(跳过物理系统) + model.update(0.016f); // 使用固定时间步长 + + // 计算模型边界和缩放比例 + ThumbnailBounds bounds = calculateThumbnailBounds(model, width, height); + + // 设置缩略图专用的正交投影(固定位置,不受摄像机影响) + Matrix3f proj = buildThumbnailProjection(width, height); + Matrix3f view = Matrix3f(1.0f); + + // 使用默认着色器 + m_defaultProgram->use(); + RenderSystem::checkGLError("thumbnail_after_use_program"); + + // 设置基础变换矩阵 + setUniformMatrix3(m_defaultProgram, "uProjectionMatrix", proj); + setUniformMatrix3(m_defaultProgram, "uViewMatrix", view); + setUniformFloatInternal(m_defaultProgram, "uCameraZ", 0.0f); // 固定Z位置 + RenderSystem::checkGLError("thumbnail_after_set_matrices"); + + // 简化光源:只使用环境光 + setupThumbnailLighting(m_defaultProgram, model); + RenderSystem::checkGLError("thumbnail_after_setup_lighting"); + + // 应用缩放和平移确保模型完全可见 + Matrix3f thumbnailTransform( + bounds.scale, 0, bounds.offsetX, + 0, bounds.scale, bounds.offsetY, + 0, 0, 1 + ); + + // 递归渲染所有根部件(应用缩略图专用变换) + for (auto& p : model.getParts()) { + if (p->getParent()) continue; + renderPartForThumbnail(p.get(), thumbnailTransform); + } + RenderSystem::checkGLError("thumbnail_after_render_parts"); + + } + catch (...) { + // 恢复原始状态 + m_renderColliders = originalRenderColliders; + m_renderLightPositions = originalRenderLightPositions; + RenderSystem::viewport(0, 0, originalViewportWidth, originalViewportHeight); + throw; + } + + RenderSystem::checkGLError("renderThumbnail_end"); +} + + +ModelRender::ThumbnailBounds ModelRender::calculateThumbnailBounds(Model2D& model, GLfloat thumbWidth, GLfloat thumbHeight) +{ + ThumbnailBounds bounds; + + // 初始化为极值 + bounds.minX = std::numeric_limits::max(); + bounds.maxX = std::numeric_limits::min(); + bounds.minY = std::numeric_limits::max(); + bounds.maxY = std::numeric_limits::min(); + + // 计算模型的世界坐标边界(递归遍历所有部件) + calculateModelBounds(model, bounds, Matrix3f(1.0f)); + + // 如果模型没有有效边界,使用默认值 + if (bounds.minX > bounds.maxX) { + bounds.minX = -50.0f; + bounds.maxX = 50.0f; + bounds.minY = -50.0f; + bounds.maxY = 50.0f; + } + + // 计算模型宽度和高度 + GLfloat modelWidth = bounds.maxX - bounds.minX; + GLfloat modelHeight = bounds.maxY - bounds.minY; + + // 计算中心点 + GLfloat centerX = (bounds.minX + bounds.maxX) * 0.5f; + GLfloat centerY = (bounds.minY + bounds.maxY) * 0.5f; + + // 计算缩放比例(考虑边距) + GLfloat margin = 0.1f; // 10%边距 + GLfloat scaleX = (thumbWidth * (1 - margin)) / modelWidth; + GLfloat scaleY = (thumbHeight * (1 - margin)) / modelHeight; + bounds.scale = std::min(scaleX, scaleY); + + // 计算偏移量(将模型中心对齐到缩略图中心) + bounds.offsetX = -centerX; + bounds.offsetY = -centerY; + + return bounds; +} + +void ModelRender::calculateModelBounds(Model2D& model, ThumbnailBounds bounds, Matrix3f parentTransform) +{ + for (auto& part : model.getParts()) { + if (part->getParent()) continue; // 只处理根部件 + + // 计算部件的世界变换 + part->updateWorldTransform(parentTransform, false); + Matrix3f worldTransform = part->getWorldTransform(); + + // 计算部件的边界 + calculatePartBounds(part.get(), bounds, worldTransform); + + // 递归处理子部件 + for (auto& child : part->getChildren()) { + calculateModelBoundsForPart(child.get(), bounds, worldTransform); + } + } +} + + +void ModelRender::calculateModelBoundsForPart(ModelPart* part, ThumbnailBounds bounds, Matrix3f parentTransform) +{ + part->updateWorldTransform(parentTransform, false); + Matrix3f worldTransform = part->getWorldTransform(); + + calculatePartBounds(part, bounds, worldTransform); + + for (auto& child : part->getChildren()) { + calculateModelBoundsForPart(child.get(), bounds, worldTransform); + } +} + + +void ModelRender::calculatePartBounds(ModelPart* part, ThumbnailBounds bounds, Matrix3f worldTransform) +{ + for (auto& mesh : part->getMeshes()) { + if (!mesh->isVisible()) continue; + + // 获取网格的顶点数据 + std::vector&& vertices = mesh->getVertices(); // 假设有这个方法获取原始顶点 + if (vertices.empty()) continue; + + // 变换顶点并更新边界 + for (int i = 0; i < vertices.size(); i += 3) { // 假设顶点格式:x, y, z + GLfloat x = vertices[i]; + GLfloat y = vertices[i + 1]; + + // 应用世界变换 + Vector3f transformed(x, y, 1.0f); + // transformed = transformed * worldTransform; + transformed = worldTransform * transformed; + + // 更新边界 + bounds.minX = std::min(bounds.minX, transformed.x); + bounds.maxX = std::max(bounds.maxX, transformed.x); + bounds.minY = std::min(bounds.minY, transformed.y); + bounds.maxY = std::max(bounds.maxY, transformed.y); + } + } +} + +ModelRender::Matrix3f ModelRender::buildThumbnailProjection(GLfloat width, GLfloat height) +{ + // 标准正交投影,不受摄像机影响 + Matrix3f m( + 2.0f / width, 0.0f, -1.0f, + 0.0f, -2.0f / height, 1.0f, + 0.0f, 0.0f, 1.0f + ); + return m; +} + +void ModelRender::renderPartForThumbnail(ModelPart* part, Matrix3f parentTransform) +{ + part->updateWorldTransform(parentTransform, false); + Matrix3f world = part->getWorldTransform(); + + setPartUniforms(m_defaultProgram, part); + setUniformMatrix3(m_defaultProgram, "uModelMatrix", world); + + for (auto& mesh : part->getMeshes()) { + renderMeshForThumbnail(mesh.get(), world); + } + + for (auto& child : part->getChildren()) { + renderPartForThumbnail(child.get(), world); + } +} + +void ModelRender::renderMeshForThumbnail(Mesh2D* mesh, Matrix3f modelMatrix) +{ + if (!mesh->isVisible()) return; + + Matrix3f matToUse = mesh->isBakedToWorld() ? Matrix3f(1.0f) : Matrix3f(modelMatrix); + + if (mesh->getTexture()) { + mesh->getTexture().bind(0); + setUniformIntInternal(m_defaultProgram, "uTexture", 0); + } + else { + RenderSystem::bindTexture(m_defaultTextureId); + setUniformIntInternal(m_defaultProgram, "uTexture", 0); + } + + setUniformMatrix3(m_defaultProgram, "uModelMatrix", matToUse); + mesh->draw(m_defaultProgram->getProgramId(), matToUse); + + RenderSystem::checkGLError("renderMeshForThumbnail"); +} + +void ModelRender::setupThumbnailLighting(ShaderProgram* sp, Model2D& model) +{ + auto& lights = model.getLights(); + int ambientLightCount = 0; + + // 查找环境光 + for (int i = 0; i < lights.size() && ambientLightCount < 1; i++) { + LightSource light = lights[i]; + if (light.isEnabled() && light.isAmbient()) { + setUniformVec2Internal(sp, "uLightsPos[0]", Vector2f(0.0f, 0.0f)); + setUniformVec3Internal(sp, "uLightsColor[0]", light.getColor()); + setUniformFloatInternal(sp, "uLightsIntensity[0]", light.getIntensity()); + setUniformIntInternal(sp, "uLightsIsAmbient[0]", 1); + setUniformIntInternal(sp, "uLightsIsGlow[0]", 0); + ambientLightCount++; + } + } + + // 如果没有环境光,创建一个默认的环境光 + if (ambientLightCount == 0) { + setUniformVec2Internal(sp, "uLightsPos[0]", Vector2f(0.0f, 0.0f)); + setUniformVec3Internal(sp, "uLightsColor[0]", Vector3f(0.8f, 0.8f, 0.8f)); + setUniformFloatInternal(sp, "uLightsIntensity[0]", 1.0f); + setUniformIntInternal(sp, "uLightsIsAmbient[0]", 1); + setUniformIntInternal(sp, "uLightsIsGlow[0]", 0); + ambientLightCount = 1; + } + + setUniformIntInternal(sp, "uLightCount", ambientLightCount); + + // 禁用所有其他光源槽位 + for (int i = ambientLightCount; i < m_maxLightCount; i++) { + std::string idxStr = std::to_string(i); + setUniformFloatInternal(sp, "uLightsIntensity[" + idxStr + "]", 0.0f); + setUniformIntInternal(sp, "uLightsIsAmbient[" + idxStr + "]", 0); + } +} + +void ModelRender::setupNonDefaultShaders(Matrix3f projection, Matrix3f view) +{ + auto& shaderList = ShaderManagement::s_shaderList; + if (shaderList.empty()) { + return; + } + + // 保存当前绑定的着色器程序 + int currentProgram = 0; + // Java: int curProgram = GL11.glGetIntegerv(GL20.GL_CURRENT_PROGRAM); + glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram); + + try { + for (auto& shader : shaderList) { + // 跳过默认着色器 + if (shader->isDefaultShader()) { + continue; + } + + try { + // 获取着色器程序 + ShaderProgram* program = ShaderManagement::getShaderProgram(shader->getShaderName()); + if (!program || program->getProgramId() == 0) { + continue; + } + + program->use(); + + // 只设置顶点坐标相关的uniform + setUniformMatrix3(program, "uProjectionMatrix", projection); + setUniformMatrix3(program, "uViewMatrix", view); + + // 设置基础模型矩阵为单位矩阵 + setUniformMatrix3(program, "uModelMatrix", Matrix3f(1.0f)); + + // 设置摄像机Z轴位置 + setUniformFloatInternal(program, "uCameraZ", m_camera.getZPosition()); + + RenderSystem::checkGLError("setupNonDefaultShaders_" + shader->getShaderName()); + + } + catch (std::exception e) { + logger.warning("Failed to setup non-default shader: {}", shader->getShaderName(), e); + } + } + } + catch (...) { + // 恢复之前绑定的着色器程序 + if (currentProgram != 0) { + glUseProgram(currentProgram); + } + throw; + } +} + +void ModelRender::renderLightPositions(Model2D& model) +{ + if (!renderLightPositions) return; + // 设置灯泡颜色为光源的颜色 + for (LightSource light : model.getLights()) { + if (!light.isEnabled()) continue; + + // 使用光源的颜色来绘制灯泡 + Vector4f lightColor(light.getColor().x, light.getColor().y, light.getColor().z, 1.0f); + setUniformVec4Internal(m_defaultProgram, "uColor", lightColor); + + // 绘制灯泡形状 + drawLightBulb(light.getPosition(), light.getIntensity()); + + if (light.isAmbient()) { + drawCrossMark(light.getPosition(), light.getIntensity()); + } + } + // 恢复原始颜色 + setUniformVec4Internal(m_defaultProgram, "uColor", Vector4f(1, 1, 1, 1)); +} + +void ModelRender::drawLightBulb(Vector2f position, GLfloat intensity) +{ + Tesselator& tesselator = Tesselator::getInstance(); + BufferBuilder& builder = tesselator.getBuilder(); + GLfloat bulbSize = 3.0f + (intensity / 10.0f); + int segments = 16; + builder.begin(GL_TRIANGLE_FAN, segments + 2); + + builder.vertex(position.x, position.y, 0.5f, 0.5f); + + for (int i = 0; i <= segments; i++) { + double angle = 2.0 * c_pi * i / segments; + GLfloat x = position.x + bulbSize * std::cosf(angle); + GLfloat y = position.y + bulbSize * std::sinf(angle); + builder.vertex(x, y, 0.5f, 0.5f); + } + tesselator.end(); +} + +void ModelRender::drawCrossMark(Vector2f position, GLfloat size) +{ + Tesselator& tesselator = Tesselator::getInstance(); + BufferBuilder& builder = tesselator.getBuilder(); + + GLfloat crossSize = size * 0.8f; + + // 绘制水平线 + builder.begin(GL_LINES, 2); + builder.vertex(position.x - crossSize, position.y, 0.5f, 0.5f); + builder.vertex(position.x + crossSize, position.y, 0.5f, 0.5f); + tesselator.end(); + + // 绘制垂直线 + builder.begin(GL_LINES, 2); + builder.vertex(position.x, position.y - crossSize, 0.5f, 0.5f); + builder.vertex(position.x, position.y + crossSize, 0.5f, 0.5f); + tesselator.end(); +} + +void ModelRender::renderPartRecursive(ModelPart* part, Matrix3f parentMat) +{ + // 确保 part 的 local/world 矩阵被计算(会更新 transformDirty) + part->updateWorldTransform(parentMat, false); + + // 直接使用已经计算好的 worldTransform + Matrix3f world = part->getWorldTransform(); + + // 先设置部件相关的 uniform(opacity / blendMode / color 等) + setPartUniforms(m_defaultProgram, part); + + // 把 world 矩阵传给 shader(uModelMatrix) + setUniformMatrix3(m_defaultProgram, "uModelMatrix", world); + + // 绘制本节点的所有 mesh(将 world 传入 renderMesh) + for (auto& mesh : part->getMeshes()) { + renderMesh(mesh.get(), world); + } + + // 递归渲染子节点,继续传入当前 world 作为子节点的 parent + for (auto& child : part->getChildren()) { + renderPartRecursive(child.get(), world); + } +} + +void ModelRender::renderMesh(Mesh2D* mesh, Matrix3f modelMatrix) +{ + if (!mesh->isVisible()) return; + + // 如果 mesh 已经被烘焙到世界坐标,则传 identity 矩阵给 shader(防止重复变换) + Matrix3f matToUse = mesh->isBakedToWorld() ? Matrix3f(1.0f) : Matrix3f(modelMatrix); + + // 手动应用摄像机偏移 + Vector2f offset = getCameraOffset(); + matToUse[2][0] -= offset.x; // 第3列第1行(平移X) + matToUse[2][1] -= offset.y; // 第3列第2行(平移Y) + + // 设置纹理相关的uniform + if (mesh->getTexture()) { + mesh->getTexture().bind(0); // 绑定到纹理单元0 + setUniformIntInternal(m_defaultProgram, "uTexture", 0); + } + else { + // 使用默认白色纹理 + RenderSystem::bindTexture(m_defaultTextureId); + setUniformIntInternal(m_defaultProgram, "uTexture", 0); + } + + // 将模型矩阵设置为当前 mesh 使用的矩阵(shader 内名为 uModelMatrix) + setUniformMatrix3(m_defaultProgram, "uModelMatrix", matToUse); + + // 调用 Mesh2D 的 draw 方法,传入当前使用的着色器程序和变换矩阵 + mesh->draw(m_defaultProgram->getProgramId(), matToUse); + + RenderSystem::checkGLError("renderMesh"); +} + +void ModelRender::renderPhysicsColliders(PhysicsSystem* physics) +{ + if (!physics) { + logger.warning("renderPhysicsColliders: physics system is nullptr"); + return; + } + + // 设置渲染状态 + RenderSystem::checkGLError("before_set_line_width"); + RenderSystem::lineWidth(m_colliderLineWidth); + RenderSystem::checkGLError("after_set_line_width"); + + RenderSystem::activeTexture(GL_TEXTURE0); + RenderSystem::bindTexture(m_defaultTextureId); + RenderSystem::checkGLError("after_bind_texture"); + + setUniformIntInternal(m_defaultProgram, "uTexture", 0); + setUniformVec4Internal(m_defaultProgram, "uColor", m_colliderColor); + setUniformFloatInternal(m_defaultProgram, "uOpacity", 1.0f); + setUniformIntInternal(m_defaultProgram, "uBlendMode", 0); + setUniformIntInternal(m_defaultProgram, "uDebugMode", 0); + RenderSystem::checkGLError("after_set_uniforms"); + + // 使用单位矩阵作为 model(碰撞体顶点按世界坐标提供) + setUniformMatrix3(m_defaultProgram, "uModelMatrix", Matrix3f(1.0f)); + RenderSystem::checkGLError("after_set_model_matrix"); + + auto& colliders = physics->getColliders(); + + if (colliders.empty()) { + logger.debug("No colliders to render"); + return; + } + + int enabledColliders = 0; + for (auto& collider : colliders) { + if (!collider->isEnabled()) continue; + + RenderSystem::checkGLError("before_render_collider_" + enabledColliders); + if (auto* c = dynamic_cast(collider.get())) { + if (c->getRadius() > 0) { + drawCircleColliderWire(c->getCenter(), c->getRadius()); + enabledColliders++; + } + else { + logger.warning("Invalid CircleCollider: center={}, radius={}", c->getCenter(), c->getRadius()); + } + } + else if (auto* r = dynamic_cast(collider.get())) { + if (r->getWidth() > 0 && r->getHeight() > 0) { + drawRectangleColliderWire(r->getCenter(), r->getWidth(), r->getHeight()); + enabledColliders++; + } + else { + logger.warning("Invalid RectangleCollider: center={}, width={}, height={}", + r->getCenter(), r->getWidth(), r->getHeight()); + } + } + else { + logger.warning("Unknown collider type: {}", collider->getClassName()); + } + + RenderSystem::checkGLError("after_render_collider_" + enabledColliders); + } + + logger.debug("Rendered {} enabled colliders", enabledColliders); + + // 恢复默认线宽 + RenderSystem::lineWidth(1.0f); + RenderSystem::checkGLError("after_reset_line_width"); +} + +void ModelRender::drawCircleColliderWire(Vector2f center, GLfloat radius) +{ + int segments = std::max(8, m_circleSegments); + + Tesselator& tesselator = Tesselator::getInstance(); + BufferBuilder& builder = tesselator.getBuilder(); + + builder.begin(GL_LINE_LOOP, segments); + for (int i = 0; i < segments; i++) { + double ang = 2.0 * c_pi * i / segments; + GLfloat x = center.x + radius * std::cosf(ang); + GLfloat y = center.y + radius * std::sinf(ang); + builder.vertex(x, y, 0.5f, 0.5f); + } + tesselator.end(); +} + +void ModelRender::drawRectangleColliderWire(Vector2f center, GLfloat width, GLfloat height) +{ + GLfloat halfW = width / 2.0f; + GLfloat halfH = height / 2.0f; + + Tesselator& tesselator = Tesselator::getInstance(); + BufferBuilder& builder = tesselator.getBuilder(); + + builder.begin(GL_LINE_LOOP, 4); + builder.vertex(center.x - halfW, center.y - halfH, 0.5f, 0.5f); + builder.vertex(center.x + halfW, center.y - halfH, 0.5f, 0.5f); + builder.vertex(center.x + halfW, center.y + halfH, 0.5f, 0.5f); + builder.vertex(center.x - halfW, center.y + halfH, 0.5f, 0.5f); + tesselator.end(); +} + +void ModelRender::setUniformIntInternal(ShaderProgram* sp, std::string name, int value) +{ + int loc = sp->getUniformLocation(name); + if (loc != -1) RenderSystem::uniform1i(loc, value); +} + +void ModelRender::setUniformVec3Internal(ShaderProgram* sp, std::string name, Vector3f vec) +{ + int loc = sp->getUniformLocation(name); + if (loc != -1) RenderSystem::uniform3f(loc, vec); +} + +void ModelRender::setUniformVec2Internal(ShaderProgram* sp, std::string name, Vector2f vec) +{ + int loc = sp->getUniformLocation(name); + if (loc != -1) RenderSystem::uniform2f(loc, vec); +} + +void ModelRender::setUniformFloatInternal(ShaderProgram* sp, std::string name, GLfloat value) +{ + int loc = sp->getUniformLocation(name); + if (loc != -1) RenderSystem::uniform1f(loc, value); +} + +void ModelRender::setUniformVec4Internal(ShaderProgram* sp, std::string name, Vector4f vec) +{ + int loc = sp->getUniformLocation(name); + if (loc != -1) RenderSystem::uniform4f(loc, vec); +} + +void ModelRender::setUniformMatrix3(ShaderProgram* sp, std::string name, Matrix3f m) +{ + int loc = sp->getUniformLocation(name); + if (loc == -1) return; + RenderSystem::uniformMatrix3(loc, m); +} + +void ModelRender::setPartUniforms(ShaderProgram* sp, ModelPart* part) +{ + setUniformFloatInternal(sp, "uOpacity", part->getOpacity()); + int blend = 0; + ModelPart::BlendMode bm = part->getBlendMode(); + switch (bm) + { + case Vivid2D::ModelPart::BlendMode::ADDITIVE: + blend = 1; + break; + case Vivid2D::ModelPart::BlendMode::MULTIPLY: + blend = 2; + break; + case Vivid2D::ModelPart::BlendMode::SCREEN: + blend = 3; + break; + default: + case Vivid2D::ModelPart::BlendMode::NORMAL: + blend = 0; + break; + } + setUniformIntInternal(sp, "uBlendMode", blend); + // 这里保留为白色,若需要部件 tint 请替换为 part 的 color 属性 + setUniformVec4Internal(sp, "uColor", Vector4f(1, 1, 1, 1)); +} + +ModelRender::Matrix3f ModelRender::buildOrthoProjection(int width, int height) +{ + // 这个投影把屏幕像素坐标(x in [0,width], y in [0,height])映射到 NDC [-1,1]x[1,-1] + Matrix3f m( + 2.0f / width, 0.0f, -1.0f, + 0.0f, -2.0f / height, 1.0f, + 0.0f, 0.0f, 1.0f + ); + return m; +} + +void ModelRender::renderText(std::string text, GLfloat x, GLfloat y, Vector4f color) +{ + if (!m_isInitialized || !m_defaultTextRenderer) return; + RenderSystem::assertOnRenderThread(); + Vector2f offset = getCameraOffset(); + GLfloat px = x - offset.x; + GLfloat py = y - offset.y; + m_defaultTextRenderer->renderText(text, px, py, color); +} + +ModelRender::Vector2f ModelRender::getCameraOffset() +{ + GLfloat width = m_viewportSize.width; + GLfloat height = m_viewportSize.height; + GLfloat zoom = m_camera.getZoom(); + Vector2f pos = m_camera.getPosition(); + GLfloat tx = -1.0f - (2.0f * zoom * pos.x / width); + GLfloat ty = 1.0f + (2.0f * zoom * pos.y / height); + GLfloat tx0 = -1.0f; + GLfloat ty0 = 1.0f; + GLfloat offsetX = tx - tx0; + GLfloat offsetY = ty - ty0; + offsetX = -offsetX * width / 2.0f / zoom; + offsetY = offsetY * height / 2.0f / zoom; + return Vector2f(offsetX, offsetY); +} + +void ModelRender::setViewport(int width, int height) +{ + m_viewportSize.width = std::max(1, width); + m_viewportSize.height = std::max(1, height); + RenderSystem::viewport(0, 0, m_viewportSize.width, m_viewportSize.height); +} diff --git a/Vivid2DRenderer/ModelRender.h b/Vivid2DRenderer/ModelRender.h new file mode 100644 index 0000000..f8ab09a --- /dev/null +++ b/Vivid2DRenderer/ModelRender.h @@ -0,0 +1,516 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "model/Mesh2D.h" +#include "systems/sources/ShaderProgram.h" +#include "systems/Camera.h" +#include "TextRenderer.h" +#include "systems/sources/CompleteShader.h" +#include "systems/sources/ShaderManagement.h" +#include "model/util/LightSource.h" +#include "model/Model2D.h" +#include "model/util/PhysicsSystem.h" +#include "systems/buffer/Tesselator.h" +#include "systems/buffer/BufferBuilder.h" + +class IllegalStateException : public std::exception { +private: + std::string message; +public: + IllegalStateException(const std::string& msg) { + this->message = msg; + } + char const* what() const noexcept override { + return message.c_str(); + } + const std::string& getMessage() const { + return message; + } + operator std::string() const { + return message; + } +}; + +// 单例模式 +class VIVID_2D_MYDLL_API ModelRender { +private: + explicit ModelRender() = default; // 默认构造函数 + ModelRender(const ModelRender& other) = delete; // 复制构造函数 + ModelRender(const ModelRender&& other) = delete; // 移动构造函数 + ~ModelRender() = default; // 默认析构函数 + +public: + // 获取全局唯一实例 + static ModelRender* instance() { + if (s_instance == nullptr) { + s_instance = new ModelRender(); + } + return s_instance; + } + + static void destroyInstance() { + if (s_instance) { + ModelRender* tmp = s_instance; + s_instance = nullptr; + delete tmp; + } + } + + // 类型定义 + typedef glm::vec2 Vector2f; + typedef glm::vec3 Vector3f; + typedef glm::vec4 Vector4f; + typedef glm::mat3 Matrix3f; + typedef Vivid2D::Mesh2D Mesh2D; + typedef Vivid2D::Model2D Model2D; + typedef Vivid2D::util::LightSource LightSource; + typedef Vivid2D::util::PhysicsSystem PhysicsSystem; + typedef Vivid2D::util::PhysicsParticle PhysicsParticle; + typedef Vivid2D::util::CircleCollider CircleCollider; + typedef Vivid2D::util::RectangleCollider RectangleCollider; + + typedef Vivid2D::ModelPart ModelPart; + typedef Vivid2D::Render::Buffer::Tesselator Tesselator; + typedef Vivid2D::Render::Buffer::BufferBuilder BufferBuilder; + + using ByteBuffer = std::vector; + + /** + * 缩略图边界计算结果 + */ + struct ThumbnailBounds { + GLfloat minX, maxX, minY, maxY; + GLfloat scale; + GLfloat offsetX, offsetY; + }; + + struct MeshGLResources { + GLuint vao = 0; + GLuint vbo = 0; + GLuint ebo = 0; + + void dispose() { + if (ebo != 0) { + glDeleteBuffers(1, &ebo); + ebo = 0; + } + if (vbo != 0) { + glDeleteBuffers(1, &vbo); + vbo = 0; + } + if (vao != 0) { + glDeleteVertexArrays(1, &vao); + vao = 0; + } + } + }; +public: + // 常量 + static constexpr double c_pi = 3.14159265358979323846; + +private: + Logger logger{ "ModelRender" }; + // 全局唯一实例 + static ModelRender* s_instance; + bool m_isInitialized = false; + struct Size { + int width; + int height; + }; + /** + * 视口大小(宽高)(像素),定义渲染区域的大小 + * 默认值:(800, 600) (像素) + * + * @see #setViewport(int, int) + * @see #setViewport(Size) + */ + Size m_viewportSize{ 800, 600 }; + /** + * 清除颜色(RGBA),用于在每帧开始时清空颜色缓冲区 + * 默认值:黑色不透明 (0.0f, 0.0f, 0.0f, 1.0f) + * + * @see RenderSystem#clearColor(GLfloat, GLfloat, GLfloat, GLfloat) + */ + Vector4f m_clearColor{ 0.0f, 0.0f, 0.0f, 1.0f }; + + /** + * 深度测试启用标志,控制是否进行深度缓冲测试 + * 在2D渲染中通常禁用以提高性能 + * 默认值:false(禁用) + * + * @see RenderSystem#enableDepthTest() + * @see RenderSystem#disableDepthTest() + */ + bool m_enableDepthTest = false; + + /** + * 混合功能启用标志,控制透明度和颜色混合 + * 默认值:true(启用) + * + * @see RenderSystem#enableBlend() + * @see RenderSystem#disableBlend() + */ + bool m_enableBlending = true; + + /** + * 最大光源数量,用于限制同时启用的光源数量 + * 默认值:80 + */ + int m_maxLightCount = 80; + + // ================== 着色器与资源管理 ================== + + /** + * 默认着色器程序,用于大多数模型的渲染 + * 包含基础的光照、纹理和变换功能 + * + * @see #compileDefaultShader() + */ + ShaderProgram* m_defaultProgram = nullptr; + + /** + * 网格GPU资源缓存,管理已上传到GPU的网格数据 + * 键:Mesh2D对象 + * 值:对应的OpenGL资源(VAO、VBO、EBO) + * + * @see MeshGLResources + */ + std::unordered_map m_meshResources; + + /** + * 纹理单元分配器,用于管理多个纹理的绑定 + * 确保不同的纹理绑定到正确的纹理单元 + * 默认从0开始递增分配 + */ + [[deprecated]]int m_textureUnitAllocator; + + /** + * 默认白色纹理ID,当模型没有指定纹理时使用 + * 这是一个1x1的纯白色纹理,确保模型有基本的颜色显示 + * + * @see #createDefaultTexture() + */ + int m_defaultTextureId = 0; + + // ================== 碰撞箱渲染配置 ================== + + /** + * 碰撞箱渲染开关,控制是否在场景中显示物理碰撞体的轮廓 + * 调试时非常有用,可以直观看到碰撞边界 + * 默认值:true(启用) + */ + bool m_renderColliders = true; + + /** + * 碰撞箱线框宽度,控制碰撞体轮廓线的粗细 + * 单位:像素 + * 默认值:1.0f + */ + GLfloat m_colliderLineWidth = 1.0f; + + /** + * 碰撞箱颜色(RGBA),定义碰撞体轮廓的显示颜色 + * 默认值:白色不透明 (1.0f, 1.0f, 1.0f, 1.0f) + */ + Vector4f m_colliderColor{ 1.0f, 1.0f, 1.0f, 1.0f }; + + /** + * 圆形碰撞体细分数量,控制圆形碰撞体的平滑度 + * 值越高圆形越平滑,但渲染开销也越大 + * 默认值:32(在性能和视觉效果间取得平衡) + */ + int m_circleSegments = 32; + + /** + * 光源位置渲染开关,控制是否在场景中显示光源的位置 + * 用点状标记显示每个启用的光源位置 + * 默认值:true(启用) + */ + bool m_renderLightPositions = true; + + // ================== 摄像机状态 ================== + + /** + * 默认摄像机,用于控制场景的视图和缩放 + * 默认位置:(0, 0) + */ + Camera m_camera{0, 0}; + + // ================== 字体管理 ================== + std::unique_ptr m_defaultTextRenderer; + Size m_fontBitmapSize{ 512, 512 }; + int m_fontFirstChar = 32; + int m_fontCharCount = 96; + +public: + // ================== 摄像机API方法 ================== + + /** + * 获取摄像机对象副本 + */ + Camera getCamera() const { + return m_camera; + } + /** + * 获取当前实例摄像机引用 + */ + Camera& getCameraReference() { + return m_camera; + } + + /** + * 设置摄像机位置 + */ + void setCameraPosition(GLfloat x, GLfloat y) { + m_camera.setPosition(x, y); + } + + /** + * 设置摄像机缩放 + */ + void setCameraZoom(GLfloat zoom) { + m_camera.setZoom(zoom); + } + + /** + * 设置摄像机Z轴位置 + */ + void setCameraZPosition(GLfloat z) { + m_camera.setZPosition(z); + } + + /** + * 移动摄像机 + */ + void moveCamera(GLfloat dx, GLfloat dy) { + m_camera.move(dx, dy); + } + + /** + * 缩放摄像机 + */ + void zoomCamera(GLfloat factor) { + m_camera.zoom(factor); + } + + /** + * 重置摄像机 + */ + void resetCamera() { + m_camera.reset(); + } + + /** + * 启用/禁用摄像机 + */ + void setCameraEnabled(bool enabled) { + m_camera.setEnabled(enabled); + } + + /** + * 构建考虑摄像机变换的投影矩阵 + */ + Matrix3f buildCameraProjection(int width, int height); + + + // ================== 初始化 / 清理 ================== + void initialize() noexcept(false); + + /** + * 初始化所有非默认着色器的基础信息(顶点坐标等) + */ + void initNonDefaultShaders(); + + /** + * 初始化着色器的基础uniforms(顶点坐标相关) + */ + void initShaderBasicUniforms(ShaderProgram* program, CompleteShader* shader); + + + auto& getTextRenderer() const { + return m_defaultTextRenderer; + } + + // ================== 工具 ================== + static Matrix3f buildOrthoProjection(int width, int height); + + /** + * 渲染文字 + * + * @param text 文字内容 + * @param x 世界坐标 X + * @param y 世界坐标 Y ,反转的 + * @param color RGBA 颜色 + */ + void renderText(std::string text, GLfloat x, GLfloat y, Vector4f color); + + /** + * 获取默认摄像机与当前摄像机之间的偏移量 + * + * @return Vector2f 偏移向量 (dx, dy) + */ + Vector2f getCameraOffset(); + + void setViewport(int width, int height); + + // ================== 辅助:外部获取状态 ================== + bool isInitialized() const { + return m_isInitialized; + } + + int getLoadedMeshCount() const { + return m_meshResources.size(); + } + + void cleanup(); + + // ================== 渲染流程 (已修改) ================== + void render(GLfloat deltaTime, Model2D& model); + + // ================== 缩略图渲染方法 ================== + + /** + * 渲染模型缩略图(图层式渲染,不受摄像机控制) + * + *

该方法提供类似PS图层预览的缩略图渲染功能:

+ *
    + *
  • 固定位置和大小,不受摄像机影响
  • + *
  • 自动缩放确保模型完全可见
  • + *
  • 禁用复杂效果以提高性能
  • + *
  • 独立的渲染状态管理
  • + *
+ * + * @param model 要渲染的模型 + * @param x 缩略图左上角X坐标(屏幕坐标) + * @param y 缩略图左上角Y坐标(屏幕坐标) + * @param width 缩略图宽度 + * @param height 缩略图高度 + */ + void renderThumbnail(Model2D& model, GLfloat x, GLfloat y, GLfloat width, GLfloat height); + +private: + + void logGLInfo(); + + void uploadLightsToShader(ShaderProgram* sp, Model2D& model); + + void setupGLState(); + + void compileDefaultShader(); + + void createDefaultTexture(); + + + /** + * 计算模型的边界和合适的缩放比例 + */ + ThumbnailBounds calculateThumbnailBounds(Model2D& model, GLfloat thumbWidth, GLfloat thumbHeight); + + /** + * 递归计算模型的边界 + */ + void calculateModelBounds(Model2D& model, ThumbnailBounds bounds, Matrix3f parentTransform); + + /** + * 递归计算部件及其子部件的边界 + */ + void calculateModelBoundsForPart(ModelPart* part, ThumbnailBounds bounds, Matrix3f parentTransform); + + /** + * 计算单个部件的边界 + */ + void calculatePartBounds(ModelPart* part, ThumbnailBounds bounds, Matrix3f worldTransform); + + /** + * 构建缩略图专用的正交投影矩阵 + */ + Matrix3f buildThumbnailProjection(GLfloat width, GLfloat height); + + /** + * 缩略图专用的部件渲染 + */ + void renderPartForThumbnail(ModelPart* part, Matrix3f parentTransform); + + /** + * 缩略图专用的网格渲染 + */ + void renderMeshForThumbnail(Mesh2D* mesh, Matrix3f modelMatrix); + + /** + * 设置缩略图专用的简化光照 + */ + void setupThumbnailLighting(ShaderProgram* sp, Model2D& model); + + + /** + * 设置所有非默认着色器的顶点坐标相关uniform + */ + void setupNonDefaultShaders(Matrix3f projection, Matrix3f view); + + void renderLightPositions(Model2D& model); + + /** + * 绘制简洁的灯泡形状 + * + * @param position 灯泡位置 + * @param intensity 光源强度,用于控制灯泡大小 + */ + void drawLightBulb(Vector2f position, GLfloat intensity); + + /** + * 绘制十字标记(用于环境光) + */ + void drawCrossMark(Vector2f position, GLfloat size); + + /** + * 关键修改点:在渲染前确保更新 part 的 worldTransform, + * 然后直接使用 part.getWorldTransform() 作为 uModelMatrix 传入 shader。 + */ + void renderPartRecursive(ModelPart* part, Matrix3f parentMat); + + void renderMesh(Mesh2D* mesh, Matrix3f modelMatrix); + + + // ================== 渲染碰撞箱相关实现 ================== + + void renderPhysicsColliders(PhysicsSystem* physics); + + + /** + * 绘制圆形碰撞框(线框) + * 使用临时 VAO/VBO,每帧创建并删除(简单实现) + */ + void drawCircleColliderWire(Vector2f center, GLfloat radius); + + /** + * 绘制矩形碰撞框(线框) + */ + void drawRectangleColliderWire(Vector2f center, GLfloat width, GLfloat height); + + // ================== uniform 设置辅助(内部使用,确保 program 已绑定) ================== + + void setUniformIntInternal(ShaderProgram* sp, std::string name, int value); + + void setUniformVec3Internal(ShaderProgram* sp, std::string name, Vector3f vec); + + void setUniformVec2Internal(ShaderProgram* sp, std::string name, Vector2f vec); + + void setUniformFloatInternal(ShaderProgram* sp, std::string name, GLfloat value); + + void setUniformVec4Internal(ShaderProgram* sp, std::string name, Vector4f vec); + + void setUniformMatrix3(ShaderProgram* sp, std::string name, Matrix3f m); + + // ================== 部件属性 ================== + void setPartUniforms(ShaderProgram* sp, ModelPart* part); + + + +}; \ No newline at end of file diff --git a/Vivid2DRenderer/Object.h b/Vivid2DRenderer/Object.h new file mode 100644 index 0000000..beb1a0c --- /dev/null +++ b/Vivid2DRenderer/Object.h @@ -0,0 +1,11 @@ +#pragma once +#include + +class Object { +public: + virtual ~Object() = default; + // RTTI + + virtual std::string getClassName() const = 0; + +}; \ No newline at end of file diff --git a/Vivid2DRenderer/TextRenderer.cpp b/Vivid2DRenderer/TextRenderer.cpp new file mode 100644 index 0000000..6733774 --- /dev/null +++ b/Vivid2DRenderer/TextRenderer.cpp @@ -0,0 +1,318 @@ +#include "pch.h" + +#include "TextRenderer.h" +#include +#include "systems/RenderSystem.h" +#include +#include +#include "AlignedMemoryStack.h" + +typedef Vivid2D::Render::Buffer::Tesselator Tesselator; +typedef Vivid2D::Render::Buffer::BufferBuilder BufferBuilder; + +Logger TextRenderer::logger{ "TextRenderer" }; +int TextRenderer::CHINESE_FIRST_CHAR = 0x4E00; // CJK Unified Ideographs 常用汉字起始范围 +int TextRenderer::CHINESE_CHAR_COUNT = 20000; + +TextRenderer::TextRenderer(int bitmapWidth, int bitmapHeight, int firstChar, int charCount) + : bitmapWidth(bitmapWidth), + bitmapHeight(bitmapHeight), + firstChar(firstChar), + charCount(charCount) +{ +} + + +/** + * 初始化字体渲染器 + */ +void TextRenderer::initialize(ByteBuffer fontData, float fontHeight) +{ + if (initialized) { + logger.warning("TextRenderer already initialized"); + return; + } + if (fontData.empty() || fontHeight <= 0) { + logger.error("Invalid font data or font height"); + return; + } + + ShaderProgram* shader = ShaderManagement::getShaderProgram("TextShader"); + if (!shader) { + logger.error("TextShader not found"); + return; + } + + shader->use(); + + try { + asciiCharData.resize(charCount); + ByteBuffer asciiBitmap(bitmapWidth * bitmapHeight); + int asciiRes = stbtt_BakeFontBitmap( + fontData.data(), + 0, fontHeight, + asciiBitmap.data(), + bitmapWidth, bitmapHeight, firstChar, charCount, + asciiCharData.data()); + if (asciiRes <= 0) { + logger.error("ASCII font bake failed, result: {}", asciiRes); + cleanup(); + return; + } + asciiTextureId = createTextureFromBitmap(bitmapWidth, bitmapHeight, asciiBitmap); + if (asciiTextureId == 0) { + logger.error("Failed to create ASCII texture"); + cleanup(); + return; + } + + // 烘焙中文 - 使用更大的纹理和正确的字符范围 + int chineseTexSize = 4096; // 中文字符需要更大的纹理 + // 分配足够的空间来存储 CHINESE_CHAR_COUNT 个字符的数据 + chineseCharData.resize(CHINESE_CHAR_COUNT); + ByteBuffer chineseBitmap(chineseTexSize * chineseTexSize); + // 关键:烘焙从 CHINESE_FIRST_CHAR 开始的 CHINESE_CHAR_COUNT 个连续字符 + int chineseRes = stbtt_BakeFontBitmap(fontData.data(), + 0, fontHeight, + chineseBitmap.data(), + chineseTexSize, chineseTexSize, CHINESE_FIRST_CHAR, CHINESE_CHAR_COUNT, + chineseCharData.data()); + if (chineseRes <= 0) { + logger.error("Chinese font bake failed, result: {}", chineseRes); + cleanup(); + return; + } + chineseTextureId = createTextureFromBitmap(chineseTexSize, chineseTexSize, chineseBitmap); + if (chineseTextureId == 0) { + logger.error("Failed to create Chinese texture"); + cleanup(); + return; + } + + initialized = true; + logger.debug("TextRenderer initialized, ASCII tex={}, Chinese tex={}", asciiTextureId, chineseTextureId); + + } + catch (std::exception e) { + logger.error("Exception during TextRenderer init: {}", e.what(), e); + cleanup(); + } + catch (...) { + logger.error("Unknown exception during TextRenderer init"); + cleanup(); + } + shader->stop(); +} + + + +/** + * 获取一行文字的宽度(带缩放) + */ +float TextRenderer::getTextWidth(std::string text, float scale) const +{ + if (!initialized || text.empty()) return 0.0f; + + float width = 0.0f; + + for (int i = 0; i < text.length(); i++) { + char c = text[i]; + if (c >= firstChar && c < firstChar + charCount) { + BakedChar bakedChar = asciiCharData[c - firstChar]; + width += bakedChar.xadvance * scale; + } + else { + // 修复中文索引逻辑:检查字符是否在烘焙的连续范围内 + if (c >= CHINESE_FIRST_CHAR && c < CHINESE_FIRST_CHAR + CHINESE_CHAR_COUNT) { + int idx = c - CHINESE_FIRST_CHAR; // 关键:使用 Unicode 差值作为索引 + BakedChar bakedChar = chineseCharData[idx]; + width += bakedChar.xadvance * scale; + } + else { + // 对于未找到的字符,使用空格宽度 + width += 0.5f * scale; // 估计值 + } + } + } + + return width; +} + +void TextRenderer::renderText(std::string text, float x, float y, Vector4f color, float scale) +{ + if (!initialized || text.empty()) return; + if (scale <= 0.0f) scale = 1.0f; + + RenderSystem::assertOnRenderThread(); + RenderSystem::pushState(); + try { + ShaderProgram* shader = ShaderManagement::getShaderProgram("TextShader"); + if (!shader) { + logger.error("TextShader not found"); + return; + } + shader->use(); + ShaderManagement::setUniformVec4(shader, "uColor", color); + ShaderManagement::setUniformInt(shader, "uTexture", 0); + + RenderSystem::enableBlend(); + RenderSystem::blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + RenderSystem::disableDepthTest(); + + thread_local AlignedMemoryStack stack; + try { + auto* q = stack.pushStackAllocate(sizeof(AlignedQuad)); + float xpos = x; + float ypos = y; + + Tesselator& t = Tesselator::getInstance(); + BufferBuilder& builder = t.getBuilder(); + + // 按字符类型分组渲染以减少纹理切换 + int currentTexture = -1; + bool batchStarted = false; + + for (int i = 0; i < text.length(); i++) { + char c = text[i]; + int targetTexture; + BakedCharBuffer charBuffer; + int texWidth, texHeight; + + if (c >= firstChar && c < firstChar + charCount) { + targetTexture = asciiTextureId; + charBuffer = asciiCharData; + texWidth = bitmapWidth; + texHeight = bitmapHeight; + stbtt_GetBakedQuad(charBuffer.data(), texWidth, texHeight, c - firstChar, &xpos, &ypos, q, true); + } + else { + // 修复中文索引逻辑:检查字符是否在烘焙的连续范围内 + if (c >= CHINESE_FIRST_CHAR && c < CHINESE_FIRST_CHAR + CHINESE_CHAR_COUNT) { + targetTexture = chineseTextureId; + charBuffer = chineseCharData; + texWidth = 4096; + texHeight = 4096; + // 关键修复:索引是字符的 Unicode 减去起始 Unicode + int idx = c - CHINESE_FIRST_CHAR; + stbtt_GetBakedQuad(charBuffer.data(), texWidth, texHeight, idx, &xpos, &ypos, q, true); + } + else { + continue; // 跳过不支持的字符 + } + } + + // 如果纹理改变,结束当前批次 + if (targetTexture != currentTexture) { + if (batchStarted) { + t.end(); + batchStarted = false; + } + RenderSystem::bindTexture(targetTexture); + currentTexture = targetTexture; + } + + // 开始新批次(如果需要) + if (!batchStarted) { + builder.begin(GL_TRIANGLES, (text.length() - i) * 6); + batchStarted = true; + } + + // 应用缩放并计算顶点 + float sx0 = x + (q->x0 - x) * scale; + float sx1 = x + (q->x1 - x) * scale; + float sy0 = y + (q->y0 - y) * scale; + float sy1 = y + (q->y1 - y) * scale; + + builder.vertex(sx0, sy0, q->s0, q->t0); + builder.vertex(sx1, sy0, q->s1, q->t0); + builder.vertex(sx0, sy1, q->s0, q->t1); + + builder.vertex(sx1, sy0, q->s1, q->t0); + builder.vertex(sx1, sy1, q->s1, q->t1); + builder.vertex(sx0, sy1, q->s0, q->t1); + } + + // 结束最后一个批次 + if (batchStarted) { + t.end(); + } + stack.popStack(); + } + catch (...) { + stack.popStack(); + throw; + } + } + catch (std::exception e) { + logger.error("Error rendering text: {}", e.what(), e); + } + catch (...) { + logger.error("Unknown error rendering text"); + } + RenderSystem::popState(); +} + +int TextRenderer::createTextureFromBitmap(int width, int height, ByteBuffer pixels) +{ + RenderSystem::assertOnRenderThread(); + try { + int textureId = RenderSystem::genTextures(); + RenderSystem::bindTexture(textureId); + + RenderSystem::pixelStore(GL_UNPACK_ALIGNMENT, 1); + RenderSystem::texImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, + GL_RED, GL_UNSIGNED_BYTE, pixels.data()); + + RenderSystem::setTextureMinFilter(GL_LINEAR); + RenderSystem::setTextureMagFilter(GL_LINEAR); + RenderSystem::setTextureWrapS(GL_CLAMP_TO_EDGE); + RenderSystem::setTextureWrapT(GL_CLAMP_TO_EDGE); + + // 设置纹理swizzle以便单通道纹理在着色器中显示为白色 + RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); + RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); + RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); + RenderSystem::texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED); + + RenderSystem::pixelStore(GL_UNPACK_ALIGNMENT, 4); + RenderSystem::bindTexture(0); + + return textureId; + } + catch (std::exception e) { + logger.error("Failed to create texture from bitmap: {}", e.what(), e); + return 0; + } + catch (...) { + logger.error("Unknown error creating texture from bitmap"); + return 0; + } +} + +void TextRenderer::cleanup() +{ + RenderSystem::assertOnRenderThread(); + if (asciiTextureId != 0) + { + RenderSystem::deleteTextures(asciiTextureId); + asciiTextureId = 0; + } + if (chineseTextureId != 0) + { + RenderSystem::deleteTextures(chineseTextureId); + chineseTextureId = 0; + } + if (!asciiCharData.empty()) + { + asciiCharData.clear(); + asciiCharData.shrink_to_fit(); + } + if (!chineseCharData.empty()) + { + chineseCharData.clear(); + chineseCharData.shrink_to_fit(); + } + initialized = false; + logger.debug("TextRenderer cleaned up"); +} + diff --git a/Vivid2DRenderer/TextRenderer.h b/Vivid2DRenderer/TextRenderer.h new file mode 100644 index 0000000..61fd02c --- /dev/null +++ b/Vivid2DRenderer/TextRenderer.h @@ -0,0 +1,80 @@ +#pragma once +#include + +#include + +#include +#include + +namespace glm { + struct vec4; +} + +class TextRenderer +{ +private: + + using Vector4f = glm::vec4; + using BakedChar = stbtt_bakedchar; + using BakedCharBuffer = std::vector; + using ByteBuffer = std::vector; + using AlignedQuad = stbtt_aligned_quad; + static Logger logger; + + int bitmapWidth = 512; + int bitmapHeight = 512; + int firstChar = 32; + int charCount = 96; + + BakedCharBuffer asciiCharData; + BakedCharBuffer chineseCharData; + int asciiTextureId = 0; + int chineseTextureId = 0; + + bool initialized = false; + + // 中文字符起始编码(选择一个不冲突的范围) + static int CHINESE_FIRST_CHAR; // CJK Unified Ideographs 常用汉字起始范围 + static int CHINESE_CHAR_COUNT; + +public: + TextRenderer(int bitmapWidth, int bitmapHeight, int firstChar, int charCount); + void initialize(ByteBuffer, float); + bool isInitialized() const; + + + float getTextWidth(std::string text, float scale) const; + /** + * 获取一行文字的宽度(单位:像素) + */ + float getTextWidth(std::string text) const { + return getTextWidth(text, 1.0f); + } + + + /** + * 渲染文字 + */ + void renderText(std::string text, float x, float y, Vector4f color, float scale); + void renderText(std::string text, float x, float y, Vector4f color) { + renderText(text, x, y, color, 1.0f); + } + + int createTextureFromBitmap(int width, int height, ByteBuffer pixels); + + void cleanup(); + + bool isInitialized() const { + return initialized; + } + + int getAsciiTextureId() const { + return asciiTextureId; + } + + int getChineseTextureId() const { + return chineseTextureId; + } + +}; + diff --git a/Vivid2DRenderer/Vivid2DRenderer.vcxproj b/Vivid2DRenderer/Vivid2DRenderer.vcxproj index f483a41..7e21f0f 100644 --- a/Vivid2DRenderer/Vivid2DRenderer.vcxproj +++ b/Vivid2DRenderer/Vivid2DRenderer.vcxproj @@ -1,4 +1,4 @@ - + @@ -70,6 +70,13 @@ + + libs\logger\include;$(ProjectDir);$(IncludePath) + libs\logger\build\Debugx64;$(LibraryPath) + + + true + Level3 @@ -112,11 +119,13 @@ true Use pch.h + stdcpp17 Windows true false + Logger_d.lib;%(AdditionalDependencies) @@ -209,6 +218,8 @@ endlocal + + @@ -221,10 +232,12 @@ endlocal + + @@ -237,8 +250,11 @@ endlocal + + + @@ -260,6 +276,7 @@ endlocal Create Create + @@ -270,6 +287,7 @@ endlocal + diff --git a/Vivid2DRenderer/Vivid2DRenderer.vcxproj.filters b/Vivid2DRenderer/Vivid2DRenderer.vcxproj.filters index 097b17f..7608870 100644 --- a/Vivid2DRenderer/Vivid2DRenderer.vcxproj.filters +++ b/Vivid2DRenderer/Vivid2DRenderer.vcxproj.filters @@ -13,6 +13,27 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {aace2215-85f9-46d7-aa76-35c982fe4c00} + + + {780d2429-421a-438d-9602-8e7b1d7e9975} + + + {1fb8ab3b-c4ca-4647-ad21-9ae610ea6d30} + + + {f052c1ce-f35a-4901-8922-116e4991b2a6} + + + {8fa89cd9-6cd6-411a-80d2-4a27e482f1c0} + + + {fba61944-faac-4a9d-a29c-fccc95a13587} + + + {555ebff3-f682-47fe-9316-2cc820ab476b} + @@ -21,9 +42,6 @@ 头文件 - - 头文件 - 头文件 @@ -60,18 +78,12 @@ 头文件 - - 头文件 - 头文件 头文件 - - 头文件 - 头文件 @@ -99,6 +111,30 @@ 头文件 + + 头文件 + + + 头文件\systems + + + 头文件\systems + + + 头文件\systems + + + 头文件\systems + + + 头文件 + + + 头文件 + + + 头文件 + @@ -107,76 +143,88 @@ 源文件 + + 源文件 + - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 - - - 源文件 + 源文件\systems - 源文件 + 源文件\systems - - 源文件 + + 源文件\systems + + + 源文件\systems + + + 源文件\systems\buffer + + + 源文件\systems\buffer + + + 源文件\systems\buffer + + + 源文件\systems\sources + + + 源文件\systems\sources + + + 源文件\systems\sources\def + + + 源文件\systems\sources\def + + + 源文件\systems\sources\def - 源文件 - - - 源文件 - - - 源文件 + 源文件\model - 源文件 + 源文件\model - - 源文件 + + 源文件\model - - 源文件 - - - 源文件 + + 源文件\model + 源文件\model + + + 源文件\model\util + + + 源文件\model\util + + + 源文件\model\util + + + 源文件\model\util + + + 源文件\model\util + + + 源文件\model\util + + + 源文件\model\util + + + 源文件\model\util + + + 源文件 + + 源文件 diff --git a/Vivid2DRenderer/libs/logger/build/Debugx64/Logger_d.lib b/Vivid2DRenderer/libs/logger/build/Debugx64/Logger_d.lib index 35f6df2..2cf08f1 100644 Binary files a/Vivid2DRenderer/libs/logger/build/Debugx64/Logger_d.lib and b/Vivid2DRenderer/libs/logger/build/Debugx64/Logger_d.lib differ diff --git a/Vivid2DRenderer/libs/logger/build/Releasex64/Logger.lib b/Vivid2DRenderer/libs/logger/build/Releasex64/Logger.lib index 6612418..83de1af 100644 Binary files a/Vivid2DRenderer/libs/logger/build/Releasex64/Logger.lib and b/Vivid2DRenderer/libs/logger/build/Releasex64/Logger.lib differ diff --git a/Vivid2DRenderer/libs/logger/include/Logger.h b/Vivid2DRenderer/libs/logger/include/Logger.h index 9684924..b1c3d9b 100644 --- a/Vivid2DRenderer/libs/logger/include/Logger.h +++ b/Vivid2DRenderer/libs/logger/include/Logger.h @@ -168,7 +168,7 @@ public: virtual ~LoggerInterface() = default; virtual void setLevel(LogLevel level) noexcept(false) = 0; virtual void setGlobalLevel(LogLevel level) noexcept(false) = 0; - virtual void level() const noexcept = 0; + virtual LogLevel level() const noexcept = 0; virtual LoggerStream trace() noexcept = 0; virtual LoggerStream debug() noexcept = 0; virtual LoggerStream info() noexcept = 0; @@ -275,6 +275,7 @@ public: ~Logger(); void setGlobalLevel(LogLevel level) noexcept(false) override; void setLevel(LogLevel level) noexcept(false) override; + LogLevel level() const noexcept override; // 流式日志打印 LoggerStream trace() noexcept override; LoggerStream debug() noexcept override; @@ -288,6 +289,41 @@ public: void warning(const std::string &msg) noexcept override; void error(const std::string &msg) noexcept override; void critical(const std::string &msg) noexcept override; + + // fmt + args + template + void trace(const std::string& fmt, Args &&...args) noexcept { + _loggerHelper(&LoggerInterface::trace, fmt, std::forward(args)...); + } + template + void debug(const std::string& fmt, Args &&...args) noexcept { + _loggerHelper(&LoggerInterface::debug, fmt, std::forward(args)...); + } + template + void info(const std::string& fmt, Args &&...args) noexcept { + _loggerHelper(&LoggerInterface::info, fmt, std::forward(args)...); + } + template + void warning(const std::string& fmt, Args &&...args) noexcept { + _loggerHelper(&LoggerInterface::warning, fmt, std::forward(args)...); + } + template + void error(const std::string& fmt, Args &&...args) noexcept { + _loggerHelper(&LoggerInterface::error, fmt, std::forward(args)...); + } + template + void critical(const std::string& fmt, Args &&...args) noexcept { + _loggerHelper(&LoggerInterface::critical, fmt, std::forward(args)...); + } + +private: + template + void _loggerHelper(void (LoggerInterface::* func)(const std::string&), const std::string& fmt, Args &&...args) noexcept { + std::string formattedMsg = LoggerFormatNS::format(fmt, std::forward(args)...); + // 处理后交给具体的日志实现 + (this->*func)(formattedMsg); + } + }; diff --git a/Vivid2DRenderer/libs/logger/src/LoggerSpdLog.h b/Vivid2DRenderer/libs/logger/src/LoggerSpdLog.h index 3f83e5e..d29429a 100644 --- a/Vivid2DRenderer/libs/logger/src/LoggerSpdLog.h +++ b/Vivid2DRenderer/libs/logger/src/LoggerSpdLog.h @@ -52,7 +52,36 @@ public: } return spdLevel; } - + static LogLevel spdLogLevelToLogLevel(SpdLogLevel level) { + LogLevel l = LogLevel::trace; + switch (level) + { + case SpdLogUtils::SpdLogLevel::trace: + l = LogLevel::trace; + break; + case SpdLogUtils::SpdLogLevel::debug: + l = LogLevel::debug; + break; + case SpdLogUtils::SpdLogLevel::info: + l = LogLevel::info; + break; + case SpdLogUtils::SpdLogLevel::warning: + l = LogLevel::warning; + break; + case SpdLogUtils::SpdLogLevel::error: + l = LogLevel::error; + break; + case SpdLogUtils::SpdLogLevel::critical: + l = LogLevel::critical; + break; + case SpdLogUtils::SpdLogLevel::off: + l = LogLevel::off; + break; + default: + break; + } + return l; + } typedef spdlog::color_mode SpdLogColorMode; static SpdLogColorMode logColorModeToSpdLogColorMode(LogColorMode colorMode) { SpdLogColorMode cm = SpdLogColorMode::automatic; @@ -268,6 +297,11 @@ void Logger::setLevel(LogLevel level) noexcept(false) } } +LogLevel Logger::level() const noexcept +{ + //level::level_enum logger::level() + return SpdLogUtils::spdLogLevelToLogLevel(static_cast(pImpl->logger->level())); +} // 带参数日志打印 void Logger::trace(const std::string &msg) noexcept diff --git a/Vivid2DRenderer/model/Model2D.h b/Vivid2DRenderer/model/Model2D.h index 76b5d0d..41b7dbc 100644 --- a/Vivid2DRenderer/model/Model2D.h +++ b/Vivid2DRenderer/model/Model2D.h @@ -382,7 +382,7 @@ namespace Vivid2D { * @brief 获取模型的物理系统。 * @return 指向物理系统的常量指针。 */ - const util::PhysicsSystem* getPhysics() const { return m_physics.get(); } + util::PhysicsSystem* getPhysics() { return m_physics.get(); } /** * @brief 获取模型当前应用的姿态。 diff --git a/Vivid2DRenderer/model/util/PhysicsSystem.h b/Vivid2DRenderer/model/util/PhysicsSystem.h index 9bff31a..b9d54a5 100644 --- a/Vivid2DRenderer/model/util/PhysicsSystem.h +++ b/Vivid2DRenderer/model/util/PhysicsSystem.h @@ -10,6 +10,8 @@ #include #include #include +#include + namespace Vivid2D { class Model2D; @@ -299,7 +301,7 @@ namespace Vivid2D::util { /** * 物理碰撞体接口 */ - struct VIVID_2D_MYDLL_API PhysicsCollider { + struct VIVID_2D_MYDLL_API PhysicsCollider : public Object { virtual ~PhysicsCollider() = default; virtual bool collidesWith(const PhysicsParticle& particle) const = 0; virtual void resolveCollision(PhysicsParticle& particle, float deltaTime) = 0; @@ -313,6 +315,7 @@ namespace Vivid2D::util { */ class VIVID_2D_MYDLL_API CircleCollider : public PhysicsCollider { public: + std::string getClassName() const override { return "CircleCollider"; } CircleCollider(std::string id, glm::vec2 center, float radius); bool collidesWith(const PhysicsParticle& particle) const override; void resolveCollision(PhysicsParticle& particle, float deltaTime) override; @@ -337,6 +340,7 @@ namespace Vivid2D::util { */ class VIVID_2D_MYDLL_API RectangleCollider : public PhysicsCollider { public: + std::string getClassName() const override { return "RectangleCollider"; } RectangleCollider(std::string id, glm::vec2 center, float width, float height); bool collidesWith(const PhysicsParticle& particle) const override; void resolveCollision(PhysicsParticle& particle, float deltaTime) override; diff --git a/Vivid2DRenderer/systems/Camera.cpp b/Vivid2DRenderer/systems/Camera.cpp new file mode 100644 index 0000000..0ae803a --- /dev/null +++ b/Vivid2DRenderer/systems/Camera.cpp @@ -0,0 +1,94 @@ +#include "Camera.h" +#include +#include + +Camera::Camera() + : m_pos(new Vector2f(0.0f, 0.0f)) +{ + +} + +Camera::Camera(float x, float y) + : m_pos(new Vector2f(x, y)) +{ + +} + +Camera::Camera(const Camera& other) + : m_pos(new Vector2f(other.m_pos->x, other.m_pos->y)) + , m_zoom(other.m_zoom) + , m_zPos(other.m_zPos) + , m_enabled(other.m_enabled) +{ + +} + +Camera::~Camera() +{ + +} + +void Camera::setPosition(float x, float y) +{ + *m_pos = Vector2f(x, y); +} + +void Camera::setPosition(Vector2f pos) +{ + *m_pos = pos; +} + +Camera::Vector2f Camera::getPosition() const +{ + return *m_pos; +} + +void Camera::setZoom(float zoom) +{ + this->m_zoom = std::max(0.1f, std::min(10.0f, zoom)); +} + +float Camera::getZoom() const +{ + return m_zoom; +} + +void Camera::setZPosition(float z) +{ + this->m_zPos = z; +} + +float Camera::getZPosition() const +{ + return m_zPos; +} + +void Camera::setEnabled(bool enabled) +{ + this->m_enabled = enabled; +} + +/** + * 获取摄像机是否启用 + */ +bool Camera::isEnabled() const +{ + return m_enabled; +} + +void Camera::move(float dx, float dy) +{ + *m_pos += Vector2f(dx, dy); +} + +void Camera::zoom(float factor) +{ + m_zoom *= factor; + m_zoom = std::max(0.1f, std::min(10.0f, m_zoom)); +} + +void Camera::reset() { + *m_pos = Vector2f(0.0f, 0.0f); + m_zoom = 1.0f; + m_zPos = 0.0f; +} diff --git a/Vivid2DRenderer/systems/Camera.h b/Vivid2DRenderer/systems/Camera.h new file mode 100644 index 0000000..9b45569 --- /dev/null +++ b/Vivid2DRenderer/systems/Camera.h @@ -0,0 +1,37 @@ +#pragma once +#include + +namespace glm { + struct vec2; +} + +class Camera { +public: + using Vector2f = glm::vec2; + +private: + std::unique_ptr m_pos; + float m_zoom = 1.0f; + float m_zPos = 0.0f; + bool m_enabled = true; + +public: + explicit Camera(); + explicit Camera(float x, float y); + Camera(const Camera& other); + ~Camera(); + void setPosition(float x, float y); + void setPosition(Vector2f); + Vector2f getPosition() const; + void setZoom(float); + void zoom(float); + float getZoom() const; + void setZPosition(float); + float getZPosition() const; + + void move(float x, float y); + void reset(); + bool isEnabled() const; + void setEnabled(bool); + +};