482 lines
14 KiB
C++
482 lines
14 KiB
C++
|
|
#include <algorithm>
|
|||
|
|
#include <event/events.h>
|
|||
|
|
#include <renderer/renderer_module.h>
|
|||
|
|
#include <utils/logger.h>
|
|||
|
|
|
|||
|
|
namespace extra2d {
|
|||
|
|
|
|||
|
|
RendererModule::RendererModule() = default;
|
|||
|
|
|
|||
|
|
RendererModule::~RendererModule() = default;
|
|||
|
|
|
|||
|
|
RendererModule::RendererModule(RendererModule &&other) noexcept
|
|||
|
|
: materialPool_(std::move(other.materialPool_)),
|
|||
|
|
meshPool_(std::move(other.meshPool_)),
|
|||
|
|
texturePool_(std::move(other.texturePool_)),
|
|||
|
|
commandBuffer_(std::move(other.commandBuffer_)),
|
|||
|
|
commandCount_(other.commandCount_),
|
|||
|
|
uniformManager_(std::move(other.uniformManager_)),
|
|||
|
|
defaultMaterialHandle_(other.defaultMaterialHandle_),
|
|||
|
|
defaultQuadHandle_(other.defaultQuadHandle_),
|
|||
|
|
defaultTextureHandle_(other.defaultTextureHandle_),
|
|||
|
|
onRenderBeginListener_(std::move(other.onRenderBeginListener_)),
|
|||
|
|
onRenderSubmitListener_(std::move(other.onRenderSubmitListener_)),
|
|||
|
|
onRenderEndListener_(std::move(other.onRenderEndListener_)),
|
|||
|
|
onResizeListener_(std::move(other.onResizeListener_)),
|
|||
|
|
onShowListener_(std::move(other.onShowListener_)),
|
|||
|
|
glInitialized_(other.glInitialized_), stats_(other.stats_),
|
|||
|
|
viewportX_(other.viewportX_), viewportY_(other.viewportY_),
|
|||
|
|
viewportWidth_(other.viewportWidth_),
|
|||
|
|
viewportHeight_(other.viewportHeight_) {
|
|||
|
|
// 重置源对象状态
|
|||
|
|
other.commandCount_ = 0;
|
|||
|
|
other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
|||
|
|
other.defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
|||
|
|
other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
|||
|
|
other.glInitialized_ = false;
|
|||
|
|
other.stats_ = {};
|
|||
|
|
other.viewportX_ = 0;
|
|||
|
|
other.viewportY_ = 0;
|
|||
|
|
other.viewportWidth_ = 0;
|
|||
|
|
other.viewportHeight_ = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
RendererModule &RendererModule::operator=(RendererModule &&other) noexcept {
|
|||
|
|
if (this != &other) {
|
|||
|
|
// 清理当前资源
|
|||
|
|
if (glInitialized_) {
|
|||
|
|
destroyDefaultResources();
|
|||
|
|
uniformManager_.shutdown();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 移动资源
|
|||
|
|
materialPool_ = std::move(other.materialPool_);
|
|||
|
|
meshPool_ = std::move(other.meshPool_);
|
|||
|
|
texturePool_ = std::move(other.texturePool_);
|
|||
|
|
commandBuffer_ = std::move(other.commandBuffer_);
|
|||
|
|
commandCount_ = other.commandCount_;
|
|||
|
|
uniformManager_ = std::move(other.uniformManager_);
|
|||
|
|
defaultMaterialHandle_ = other.defaultMaterialHandle_;
|
|||
|
|
defaultQuadHandle_ = other.defaultQuadHandle_;
|
|||
|
|
defaultTextureHandle_ = other.defaultTextureHandle_;
|
|||
|
|
onRenderBeginListener_ = std::move(other.onRenderBeginListener_);
|
|||
|
|
onRenderSubmitListener_ = std::move(other.onRenderSubmitListener_);
|
|||
|
|
onRenderEndListener_ = std::move(other.onRenderEndListener_);
|
|||
|
|
onResizeListener_ = std::move(other.onResizeListener_);
|
|||
|
|
onShowListener_ = std::move(other.onShowListener_);
|
|||
|
|
glInitialized_ = other.glInitialized_;
|
|||
|
|
stats_ = other.stats_;
|
|||
|
|
viewportX_ = other.viewportX_;
|
|||
|
|
viewportY_ = other.viewportY_;
|
|||
|
|
viewportWidth_ = other.viewportWidth_;
|
|||
|
|
viewportHeight_ = other.viewportHeight_;
|
|||
|
|
|
|||
|
|
// 重置源对象状态
|
|||
|
|
other.commandCount_ = 0;
|
|||
|
|
other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
|||
|
|
other.defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
|||
|
|
other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
|||
|
|
other.glInitialized_ = false;
|
|||
|
|
other.stats_ = {};
|
|||
|
|
other.viewportX_ = 0;
|
|||
|
|
other.viewportY_ = 0;
|
|||
|
|
other.viewportWidth_ = 0;
|
|||
|
|
other.viewportHeight_ = 0;
|
|||
|
|
}
|
|||
|
|
return *this;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool RendererModule::init() {
|
|||
|
|
E2D_LOG_INFO("Initializing RendererModule...");
|
|||
|
|
|
|||
|
|
// 绑定事件监听器
|
|||
|
|
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
|||
|
|
onRenderSubmitListener_.bind(
|
|||
|
|
[this](const RenderCommand &cmd) { onRenderSubmit(cmd); });
|
|||
|
|
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
|||
|
|
onResizeListener_.bind([this](int32_t w, int32_t h) { onResize(w, h); });
|
|||
|
|
|
|||
|
|
// 延迟 GL 初始化到窗口显示时
|
|||
|
|
onShowListener_.bind([this]() { onWindowShow(); });
|
|||
|
|
|
|||
|
|
E2D_LOG_INFO("RendererModule initialized (waiting for GL context)");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::onWindowShow() {
|
|||
|
|
if (glInitialized_) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
E2D_LOG_INFO("Initializing OpenGL context...");
|
|||
|
|
|
|||
|
|
// 初始化 UBO 管理器(需要 GL 上下文)
|
|||
|
|
if (!uniformManager_.initialize()) {
|
|||
|
|
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建默认资源(需要 GL 上下文)
|
|||
|
|
if (!createDefaultResources()) {
|
|||
|
|
E2D_LOG_ERROR("Failed to create default resources");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置默认视口
|
|||
|
|
setViewport(0, 0, 800, 600);
|
|||
|
|
|
|||
|
|
// 启用深度测试和混合
|
|||
|
|
glEnable(GL_DEPTH_TEST);
|
|||
|
|
glEnable(GL_BLEND);
|
|||
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|||
|
|
|
|||
|
|
glInitialized_ = true;
|
|||
|
|
E2D_LOG_INFO("OpenGL context initialized successfully");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::shutdown() {
|
|||
|
|
E2D_LOG_INFO("Shutting down RendererModule...");
|
|||
|
|
|
|||
|
|
// 只有在 GL 初始化后才销毁 GL 相关资源
|
|||
|
|
if (glInitialized_) {
|
|||
|
|
// 销毁默认资源
|
|||
|
|
destroyDefaultResources();
|
|||
|
|
|
|||
|
|
// 关闭 UBO 管理器
|
|||
|
|
uniformManager_.shutdown();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清理资源池
|
|||
|
|
materialPool_.slots.clear();
|
|||
|
|
while (!materialPool_.freeIndices.empty())
|
|||
|
|
materialPool_.freeIndices.pop();
|
|||
|
|
|
|||
|
|
meshPool_.slots.clear();
|
|||
|
|
while (!meshPool_.freeIndices.empty())
|
|||
|
|
meshPool_.freeIndices.pop();
|
|||
|
|
|
|||
|
|
texturePool_.slots.clear();
|
|||
|
|
while (!texturePool_.freeIndices.empty())
|
|||
|
|
texturePool_.freeIndices.pop();
|
|||
|
|
|
|||
|
|
glInitialized_ = false;
|
|||
|
|
|
|||
|
|
E2D_LOG_INFO("RendererModule shutdown complete");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MaterialHandle RendererModule::registerMaterial(Ptr<Material> material) {
|
|||
|
|
uint64_t handle = materialPool_.acquire();
|
|||
|
|
auto *slot = materialPool_.get(handle);
|
|||
|
|
if (slot) {
|
|||
|
|
slot->material = material;
|
|||
|
|
}
|
|||
|
|
return handle;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MeshHandle RendererModule::registerMesh(Ptr<Mesh> mesh) {
|
|||
|
|
uint64_t handle = meshPool_.acquire();
|
|||
|
|
auto *slot = meshPool_.get(handle);
|
|||
|
|
if (slot) {
|
|||
|
|
slot->mesh = mesh;
|
|||
|
|
}
|
|||
|
|
return handle;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TextureHandle RendererModule::registerTexture(Ptr<Texture> texture) {
|
|||
|
|
uint64_t handle = texturePool_.acquire();
|
|||
|
|
auto *slot = texturePool_.get(handle);
|
|||
|
|
if (slot) {
|
|||
|
|
slot->texture = texture;
|
|||
|
|
}
|
|||
|
|
return handle;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::unregisterMaterial(MaterialHandle handle) {
|
|||
|
|
materialPool_.release(handle);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::unregisterMesh(MeshHandle handle) {
|
|||
|
|
meshPool_.release(handle);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::unregisterTexture(TextureHandle handle) {
|
|||
|
|
texturePool_.release(handle);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Ptr<Material> RendererModule::getMaterial(MaterialHandle handle) {
|
|||
|
|
auto *slot = materialPool_.get(handle);
|
|||
|
|
return slot ? slot->material : nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Ptr<Mesh> RendererModule::getMesh(MeshHandle handle) {
|
|||
|
|
auto *slot = meshPool_.get(handle);
|
|||
|
|
return slot ? slot->mesh : nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Ptr<Texture> RendererModule::getTexture(TextureHandle handle) {
|
|||
|
|
auto *slot = texturePool_.get(handle);
|
|||
|
|
return slot ? slot->texture : nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
|||
|
|
viewportX_ = x;
|
|||
|
|
viewportY_ = y;
|
|||
|
|
viewportWidth_ = width;
|
|||
|
|
viewportHeight_ = height;
|
|||
|
|
glViewport(x, y, width, height);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::clear(const Color &color, uint32_t flags) {
|
|||
|
|
GLbitfield mask = 0;
|
|||
|
|
|
|||
|
|
if (flags & CLEAR_COLOR_FLAG) {
|
|||
|
|
glClearColor(color.r, color.g, color.b, color.a);
|
|||
|
|
mask |= GL_COLOR_BUFFER_BIT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (flags & CLEAR_DEPTH_FLAG) {
|
|||
|
|
mask |= GL_DEPTH_BUFFER_BIT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (flags & CLEAR_STENCIL_FLAG) {
|
|||
|
|
mask |= GL_STENCIL_BUFFER_BIT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (mask != 0) {
|
|||
|
|
glClear(mask);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::onRenderBegin() {
|
|||
|
|
// 如果 GL 未初始化,跳过渲染
|
|||
|
|
if (!glInitialized_) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清空命令缓冲区
|
|||
|
|
commandCount_ = 0;
|
|||
|
|
|
|||
|
|
// 重置统计
|
|||
|
|
stats_ = {};
|
|||
|
|
|
|||
|
|
// 重置 UBO 管理器
|
|||
|
|
uniformManager_.resetMaterialUBOs();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
|||
|
|
// 如果 GL 未初始化,跳过提交
|
|||
|
|
if (!glInitialized_) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查缓冲区是否已满
|
|||
|
|
if (commandCount_ >= MAX_RENDER_COMMANDS) {
|
|||
|
|
E2D_LOG_WARN("Render command buffer full!");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 直接存入预分配缓冲区(无动态分配)
|
|||
|
|
commandBuffer_[commandCount_++] = cmd;
|
|||
|
|
stats_.commandsSubmitted++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::onRenderEnd() {
|
|||
|
|
// 如果 GL 未初始化,跳过渲染
|
|||
|
|
if (!glInitialized_) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 排序命令
|
|||
|
|
sortCommands();
|
|||
|
|
|
|||
|
|
// 批处理并绘制
|
|||
|
|
batchAndDraw();
|
|||
|
|
|
|||
|
|
// 输出统计
|
|||
|
|
E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches",
|
|||
|
|
stats_.commandsExecuted, stats_.drawCalls, stats_.batches);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::onResize(int32 width, int32 height) {
|
|||
|
|
setViewport(0, 0, width, height);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::sortCommands() {
|
|||
|
|
// 使用 std::sort 对命令进行排序
|
|||
|
|
std::sort(commandBuffer_.begin(), commandBuffer_.begin() + commandCount_,
|
|||
|
|
[](const RenderCommand &a, const RenderCommand &b) {
|
|||
|
|
return a.sortKey < b.sortKey;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::batchAndDraw() {
|
|||
|
|
MaterialHandle lastMaterial = INVALID_MATERIAL_HANDLE;
|
|||
|
|
MeshHandle lastMesh = INVALID_MESH_HANDLE;
|
|||
|
|
uint32_t batchStart = 0;
|
|||
|
|
uint32_t batchCount = 0;
|
|||
|
|
|
|||
|
|
for (uint32_t i = 0; i < commandCount_; ++i) {
|
|||
|
|
const auto &cmd = commandBuffer_[i];
|
|||
|
|
|
|||
|
|
if (cmd.type != RenderCommandType::DrawMesh) {
|
|||
|
|
// 处理非绘制命令
|
|||
|
|
if (batchCount > 0) {
|
|||
|
|
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
|||
|
|
stats_.batches++;
|
|||
|
|
batchCount = 0;
|
|||
|
|
}
|
|||
|
|
executeCommand(cmd);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否需要刷新批次
|
|||
|
|
if (cmd.drawMesh.material != lastMaterial ||
|
|||
|
|
cmd.drawMesh.mesh != lastMesh) {
|
|||
|
|
|
|||
|
|
// 刷新上一批次
|
|||
|
|
if (batchCount > 0) {
|
|||
|
|
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
|||
|
|
stats_.batches++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
lastMaterial = cmd.drawMesh.material;
|
|||
|
|
lastMesh = cmd.drawMesh.mesh;
|
|||
|
|
batchStart = i;
|
|||
|
|
batchCount = 1;
|
|||
|
|
} else {
|
|||
|
|
++batchCount;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stats_.commandsExecuted++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 刷新最后一批
|
|||
|
|
if (batchCount > 0) {
|
|||
|
|
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
|||
|
|
stats_.batches++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::drawBatch(uint32_t start, uint32_t count,
|
|||
|
|
MaterialHandle materialHandle,
|
|||
|
|
MeshHandle meshHandle) {
|
|||
|
|
auto material = getMaterial(materialHandle);
|
|||
|
|
auto mesh = getMesh(meshHandle);
|
|||
|
|
|
|||
|
|
if (!material || !mesh)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 应用材质(绑定着色器、上传 UBO、绑定纹理)
|
|||
|
|
material->apply(uniformManager_);
|
|||
|
|
|
|||
|
|
// 绑定网格
|
|||
|
|
mesh->bind();
|
|||
|
|
|
|||
|
|
if (count == 1) {
|
|||
|
|
// 单绘制
|
|||
|
|
mesh->draw();
|
|||
|
|
} else {
|
|||
|
|
// 批量绘制 - 使用实例化渲染
|
|||
|
|
// 上传变换矩阵到 UBO
|
|||
|
|
std::vector<float> transforms;
|
|||
|
|
transforms.reserve(count * 16);
|
|||
|
|
|
|||
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|||
|
|
const auto &transform = commandBuffer_[start + i].drawMesh.transform;
|
|||
|
|
// 将 Transform 转换为 4x4 矩阵并添加到 transforms
|
|||
|
|
// 这里简化处理,实际需要完整的矩阵转换
|
|||
|
|
float matrix[16];
|
|||
|
|
transform.toMatrix(matrix);
|
|||
|
|
transforms.insert(transforms.end(), matrix, matrix + 16);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新实例 UBO
|
|||
|
|
auto *instanceUBO = uniformManager_.acquireMaterialUBO(
|
|||
|
|
static_cast<uint32_t>(transforms.size() * sizeof(float)));
|
|||
|
|
if (instanceUBO) {
|
|||
|
|
instanceUBO->update(
|
|||
|
|
transforms.data(),
|
|||
|
|
static_cast<uint32_t>(transforms.size() * sizeof(float)));
|
|||
|
|
instanceUBO->bind(INSTANCE_UBO_BINDING);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 实例化渲染
|
|||
|
|
mesh->drawInstanced(count);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
stats_.drawCalls++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::executeCommand(const RenderCommand &cmd) {
|
|||
|
|
switch (cmd.type) {
|
|||
|
|
case RenderCommandType::SetViewport:
|
|||
|
|
setViewport(cmd.viewport.x, cmd.viewport.y, cmd.viewport.width,
|
|||
|
|
cmd.viewport.height);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case RenderCommandType::Clear:
|
|||
|
|
clear(cmd.clear.color, cmd.clear.flags);
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool RendererModule::createDefaultResources() {
|
|||
|
|
// 创建默认着色器
|
|||
|
|
auto defaultShader = makePtr<Shader>();
|
|||
|
|
if (!defaultShader->loadFromFile("shader/default.vert",
|
|||
|
|
"shader/default.frag")) {
|
|||
|
|
E2D_LOG_ERROR("Failed to load default shader");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建默认材质布局
|
|||
|
|
auto defaultLayout = makePtr<MaterialLayout>();
|
|||
|
|
defaultLayout->addParam("tintColor", MaterialParamType::Color);
|
|||
|
|
defaultLayout->addParam("opacity", MaterialParamType::Float);
|
|||
|
|
defaultLayout->finalize();
|
|||
|
|
|
|||
|
|
// 创建默认材质
|
|||
|
|
auto defaultMaterial = makePtr<Material>();
|
|||
|
|
defaultMaterial->setLayout(defaultLayout);
|
|||
|
|
defaultMaterial->setShader(defaultShader);
|
|||
|
|
defaultMaterial->setColor("tintColor", Color::White);
|
|||
|
|
defaultMaterial->setFloat("opacity", 1.0f);
|
|||
|
|
|
|||
|
|
defaultMaterialHandle_ = registerMaterial(defaultMaterial);
|
|||
|
|
|
|||
|
|
// 创建默认四边形
|
|||
|
|
auto defaultQuad = Mesh::createQuad(Vec2(1.0f, 1.0f));
|
|||
|
|
defaultQuadHandle_ = registerMesh(defaultQuad);
|
|||
|
|
|
|||
|
|
// 创建默认纹理(1x1 白色)
|
|||
|
|
auto defaultTexture = makePtr<Texture>();
|
|||
|
|
uint8_t whitePixel[4] = {255, 255, 255, 255};
|
|||
|
|
defaultTexture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8);
|
|||
|
|
defaultTextureHandle_ = registerTexture(defaultTexture);
|
|||
|
|
|
|||
|
|
E2D_LOG_INFO("Default resources created");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void RendererModule::destroyDefaultResources() {
|
|||
|
|
if (defaultMaterialHandle_ != INVALID_MATERIAL_HANDLE) {
|
|||
|
|
unregisterMaterial(defaultMaterialHandle_);
|
|||
|
|
defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (defaultQuadHandle_ != INVALID_MESH_HANDLE) {
|
|||
|
|
unregisterMesh(defaultQuadHandle_);
|
|||
|
|
defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (defaultTextureHandle_ != INVALID_TEXTURE_HANDLE) {
|
|||
|
|
unregisterTexture(defaultTextureHandle_);
|
|||
|
|
defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace extra2d
|