feat(渲染): 实现节点层级变换支持
refactor(场景图): 简化形状节点渲染逻辑 feat(输入): 添加输入事件到事件服务的连接 docs(模块系统): 更新文档说明模块配置和平台支持 test(示例): 添加场景图测试示例展示节点变换
This commit is contained in:
parent
8c56c29cd2
commit
6c6cac55f7
|
|
@ -22,7 +22,9 @@
|
|||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/platform/iwindow.h>
|
||||
#include <extra2d/platform/keys.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/platform/platform_module.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
|
||||
// Graphics
|
||||
#include <extra2d/graphics/camera.h>
|
||||
|
|
@ -54,6 +56,12 @@
|
|||
#include <extra2d/utils/random.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
|
||||
// Services
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/services/scene_service.h>
|
||||
#include <extra2d/services/timer_service.h>
|
||||
#include <extra2d/services/camera_service.h>
|
||||
|
||||
// Application
|
||||
#include <extra2d/app/application.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <extra2d/graphics/render_config.h>
|
||||
#include <extra2d/graphics/vram_manager.h>
|
||||
#include <extra2d/platform/iinput.h>
|
||||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/platform/platform_init_module.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/scene_service.h>
|
||||
|
|
@ -53,6 +54,7 @@ bool Application::init(const AppConfig& config) {
|
|||
register_config_module();
|
||||
register_platform_module();
|
||||
register_window_module();
|
||||
register_input_module();
|
||||
register_render_module();
|
||||
|
||||
auto* configInit = ModuleRegistry::instance().getInitializer(get_config_module_id());
|
||||
|
|
@ -74,6 +76,7 @@ bool Application::init(const std::string& configPath) {
|
|||
register_config_module();
|
||||
register_platform_module();
|
||||
register_window_module();
|
||||
register_input_module();
|
||||
register_render_module();
|
||||
|
||||
auto* configInit = ModuleRegistry::instance().getInitializer(get_config_module_id());
|
||||
|
|
@ -90,7 +93,7 @@ bool Application::init(const std::string& configPath) {
|
|||
void Application::registerCoreServices() {
|
||||
auto& locator = ServiceLocator::instance();
|
||||
|
||||
if (!locator.hasService<IEventService>()) {
|
||||
if (!locator.hasService<ISceneService>()) {
|
||||
locator.registerService<ISceneService>(makeShared<SceneService>());
|
||||
}
|
||||
|
||||
|
|
@ -98,10 +101,6 @@ void Application::registerCoreServices() {
|
|||
locator.registerService<ITimerService>(makeShared<TimerService>());
|
||||
}
|
||||
|
||||
if (!locator.hasService<IEventService>()) {
|
||||
locator.registerService<IEventService>(makeShared<EventService>());
|
||||
}
|
||||
|
||||
if (!locator.hasService<ICameraService>()) {
|
||||
auto cameraService = makeShared<CameraService>();
|
||||
if (window_) {
|
||||
|
|
@ -119,6 +118,12 @@ void Application::registerCoreServices() {
|
|||
}
|
||||
|
||||
bool Application::initModules() {
|
||||
auto& locator = ServiceLocator::instance();
|
||||
|
||||
if (!locator.hasService<IEventService>()) {
|
||||
locator.registerService<IEventService>(makeShared<EventService>());
|
||||
}
|
||||
|
||||
auto initOrder = ModuleRegistry::instance().getInitializationOrder();
|
||||
|
||||
for (ModuleId moduleId : initOrder) {
|
||||
|
|
|
|||
|
|
@ -698,9 +698,15 @@ void GLRenderer::addShapeVertex(float x, float y, const Color &color) {
|
|||
if (shapeVertexCount_ >= MAX_SHAPE_VERTICES) {
|
||||
flushShapeBatch();
|
||||
}
|
||||
|
||||
glm::vec4 pos(x, y, 0.0f, 1.0f);
|
||||
if (!transformStack_.empty()) {
|
||||
pos = transformStack_.back() * pos;
|
||||
}
|
||||
|
||||
ShapeVertex &v = shapeVertexCache_[shapeVertexCount_++];
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.x = pos.x;
|
||||
v.y = pos.y;
|
||||
v.r = color.r;
|
||||
v.g = color.g;
|
||||
v.b = color.b;
|
||||
|
|
@ -717,9 +723,15 @@ void GLRenderer::addLineVertex(float x, float y, const Color &color) {
|
|||
if (lineVertexCount_ >= MAX_LINE_VERTICES) {
|
||||
flushLineBatch();
|
||||
}
|
||||
|
||||
glm::vec4 pos(x, y, 0.0f, 1.0f);
|
||||
if (!transformStack_.empty()) {
|
||||
pos = transformStack_.back() * pos;
|
||||
}
|
||||
|
||||
ShapeVertex &v = lineVertexCache_[lineVertexCount_++];
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.x = pos.x;
|
||||
v.y = pos.y;
|
||||
v.r = color.r;
|
||||
v.g = color.g;
|
||||
v.b = color.b;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
#include <extra2d/platform/input_module.h>
|
||||
#include <extra2d/config/module_registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/utils/logger.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "backends/sdl2/sdl2_input.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace extra2d {
|
||||
|
|
@ -146,6 +149,20 @@ bool InputModuleInitializer::initialize(const IModuleConfig* config) {
|
|||
return false;
|
||||
}
|
||||
|
||||
SDL2Input* sdl2Input = dynamic_cast<SDL2Input*>(input_);
|
||||
if (sdl2Input) {
|
||||
auto eventService = ServiceLocator::instance().getService<IEventService>();
|
||||
if (eventService) {
|
||||
sdl2Input->setEventCallback([eventService](const Event& event) {
|
||||
Event mutableEvent = event;
|
||||
eventService->dispatch(mutableEvent);
|
||||
});
|
||||
E2D_LOG_INFO("Input events connected to EventService");
|
||||
} else {
|
||||
E2D_LOG_WARN("EventService not available - input events will not be dispatched");
|
||||
}
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("Input module initialized");
|
||||
E2D_LOG_INFO(" Deadzone: {}", config_.deadzone);
|
||||
|
|
|
|||
|
|
@ -520,11 +520,15 @@ void Node::onRender(RenderBackend &renderer) {
|
|||
if (!visible_)
|
||||
return;
|
||||
|
||||
renderer.pushTransform(getLocalTransform());
|
||||
|
||||
onDraw(renderer);
|
||||
|
||||
for (auto &child : children_) {
|
||||
child->onRender(renderer);
|
||||
}
|
||||
|
||||
renderer.popTransform();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -268,25 +268,24 @@ Rect ShapeNode::getBounds() const {
|
|||
* @param renderer 渲染后端引用
|
||||
*
|
||||
* 根据形状类型调用相应的渲染方法进行绘制
|
||||
* 注意:变换矩阵已由 Node::onRender 通过 pushTransform 应用,
|
||||
* 此处直接使用本地坐标即可。
|
||||
*/
|
||||
void ShapeNode::onDraw(RenderBackend &renderer) {
|
||||
if (points_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vec2 offset = getPosition();
|
||||
|
||||
switch (shapeType_) {
|
||||
case ShapeType::Point:
|
||||
if (!points_.empty()) {
|
||||
renderer.fillCircle(points_[0] + offset, lineWidth_ * 0.5f, color_, 8);
|
||||
renderer.fillCircle(points_[0], lineWidth_ * 0.5f, color_, 8);
|
||||
}
|
||||
break;
|
||||
|
||||
case ShapeType::Line:
|
||||
if (points_.size() >= 2) {
|
||||
renderer.drawLine(points_[0] + offset, points_[1] + offset, color_,
|
||||
lineWidth_);
|
||||
renderer.drawLine(points_[0], points_[1], color_, lineWidth_);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -295,11 +294,11 @@ void ShapeNode::onDraw(RenderBackend &renderer) {
|
|||
if (filled_) {
|
||||
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
|
||||
points_[2].y - points_[0].y);
|
||||
renderer.fillRect(Rect(rect.origin + offset, rect.size), color_);
|
||||
renderer.fillRect(rect, color_);
|
||||
} else {
|
||||
for (size_t i = 0; i < points_.size(); ++i) {
|
||||
Vec2 start = points_[i] + offset;
|
||||
Vec2 end = points_[(i + 1) % points_.size()] + offset;
|
||||
Vec2 start = points_[i];
|
||||
Vec2 end = points_[(i + 1) % points_.size()];
|
||||
renderer.drawLine(start, end, color_, lineWidth_);
|
||||
}
|
||||
}
|
||||
|
|
@ -310,41 +309,31 @@ void ShapeNode::onDraw(RenderBackend &renderer) {
|
|||
if (points_.size() >= 2) {
|
||||
float radius = points_[1].x;
|
||||
if (filled_) {
|
||||
renderer.fillCircle(points_[0] + offset, radius, color_, segments_);
|
||||
renderer.fillCircle(points_[0], radius, color_, segments_);
|
||||
} else {
|
||||
renderer.drawCircle(points_[0] + offset, radius, color_, segments_,
|
||||
lineWidth_);
|
||||
renderer.drawCircle(points_[0], radius, color_, segments_, lineWidth_);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ShapeType::Triangle:
|
||||
if (points_.size() >= 3) {
|
||||
Vec2 p1 = points_[0] + offset;
|
||||
Vec2 p2 = points_[1] + offset;
|
||||
Vec2 p3 = points_[2] + offset;
|
||||
if (filled_) {
|
||||
renderer.fillTriangle(p1, p2, p3, color_);
|
||||
renderer.fillTriangle(points_[0], points_[1], points_[2], color_);
|
||||
} else {
|
||||
renderer.drawLine(p1, p2, color_, lineWidth_);
|
||||
renderer.drawLine(p2, p3, color_, lineWidth_);
|
||||
renderer.drawLine(p3, p1, color_, lineWidth_);
|
||||
renderer.drawLine(points_[0], points_[1], color_, lineWidth_);
|
||||
renderer.drawLine(points_[1], points_[2], color_, lineWidth_);
|
||||
renderer.drawLine(points_[2], points_[0], color_, lineWidth_);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ShapeType::Polygon:
|
||||
if (!points_.empty()) {
|
||||
std::vector<Vec2> transformedPoints;
|
||||
transformedPoints.reserve(points_.size());
|
||||
for (const auto &p : points_) {
|
||||
transformedPoints.push_back(p + offset);
|
||||
}
|
||||
|
||||
if (filled_) {
|
||||
renderer.fillPolygon(transformedPoints, color_);
|
||||
renderer.fillPolygon(points_, color_);
|
||||
} else {
|
||||
renderer.drawPolygon(transformedPoints, color_, lineWidth_);
|
||||
renderer.drawPolygon(points_, color_, lineWidth_);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,30 +1,112 @@
|
|||
/**
|
||||
* @file main.cpp
|
||||
* @brief Extra2D 基础示例程序
|
||||
* @brief Extra2D 场景图测试示例
|
||||
*
|
||||
* 演示如何使用 Extra2D 引擎创建一个简单的窗口应用程序。
|
||||
* 演示场景图功能:
|
||||
* - 节点层级关系
|
||||
* - 变换(位置、旋转、缩放)
|
||||
* - 形状节点渲染
|
||||
* - 输入事件处理
|
||||
*/
|
||||
|
||||
#include <extra2d/extra2d.h>
|
||||
#include <extra2d/graphics/render_config.h>
|
||||
#include <extra2d/platform/window_config.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
/**
|
||||
* @brief 创建场景图测试
|
||||
*/
|
||||
void createSceneGraph(Scene *scene) {
|
||||
float width = scene->getWidth();
|
||||
float height = scene->getHeight();
|
||||
|
||||
auto root = makeShared<Node>();
|
||||
root->setName("Root");
|
||||
root->setPos(width / 2, height / 2);
|
||||
scene->addChild(root);
|
||||
|
||||
auto parent1 = makeShared<Node>();
|
||||
parent1->setName("Parent1");
|
||||
parent1->setPos(-200, 0);
|
||||
root->addChild(parent1);
|
||||
|
||||
auto rect1 = ShapeNode::createFilledRect(Rect(-50, -50, 100, 100),
|
||||
Color(1.0f, 0.4f, 0.4f, 1.0f));
|
||||
rect1->setName("RedRect");
|
||||
parent1->addChild(rect1);
|
||||
|
||||
auto child1 = makeShared<Node>();
|
||||
child1->setName("Child1");
|
||||
child1->setPos(80, 0);
|
||||
child1->setRotation(45);
|
||||
child1->setScale(0.5f);
|
||||
parent1->addChild(child1);
|
||||
|
||||
auto smallRect = ShapeNode::createFilledRect(Rect(-30, -30, 60, 60),
|
||||
Color(1.0f, 0.8f, 0.4f, 1.0f));
|
||||
smallRect->setName("OrangeRect");
|
||||
child1->addChild(smallRect);
|
||||
|
||||
auto parent2 = makeShared<Node>();
|
||||
parent2->setName("Parent2");
|
||||
parent2->setPos(200, 0);
|
||||
root->addChild(parent2);
|
||||
|
||||
auto circle1 = ShapeNode::createFilledCircle(Vec2(0, 0), 60,
|
||||
Color(0.4f, 0.4f, 1.0f, 1.0f));
|
||||
circle1->setName("BlueCircle");
|
||||
parent2->addChild(circle1);
|
||||
|
||||
auto child2 = makeShared<Node>();
|
||||
child2->setName("Child2");
|
||||
child2->setPos(0, 100);
|
||||
parent2->addChild(child2);
|
||||
|
||||
auto triangle = ShapeNode::createFilledTriangle(
|
||||
Vec2(0, -40), Vec2(-35, 30), Vec2(35, 30), Color(0.4f, 1.0f, 0.4f, 1.0f));
|
||||
triangle->setName("GreenTriangle");
|
||||
child2->addChild(triangle);
|
||||
|
||||
auto line = ShapeNode::createLine(Vec2(-300, -200), Vec2(300, -200),
|
||||
Color(1.0f, 1.0f, 1.0f, 1.0f), 2.0f);
|
||||
line->setName("BottomLine");
|
||||
root->addChild(line);
|
||||
|
||||
auto polygon = ShapeNode::createFilledPolygon(
|
||||
{Vec2(0, -50), Vec2(50, 0), Vec2(30, 50), Vec2(-30, 50), Vec2(-50, 0)},
|
||||
Color(1.0f, 0.4f, 1.0f, 1.0f));
|
||||
polygon->setName("PurplePolygon");
|
||||
polygon->setPos(0, -150);
|
||||
root->addChild(polygon);
|
||||
|
||||
std::cout << "\n=== Scene Graph Structure ===" << std::endl;
|
||||
std::cout << "Scene (root)" << std::endl;
|
||||
std::cout << " └── Root (center)" << std::endl;
|
||||
std::cout << " ├── Parent1 (left)" << std::endl;
|
||||
std::cout << " │ ├── RedRect (100x100)" << std::endl;
|
||||
std::cout << " │ └── Child1 (rotated 45°, scaled 0.5)" << std::endl;
|
||||
std::cout << " │ └── OrangeRect (60x60)" << std::endl;
|
||||
std::cout << " ├── Parent2 (right)" << std::endl;
|
||||
std::cout << " │ ├── BlueCircle (radius 60)" << std::endl;
|
||||
std::cout << " │ └── Child2 (below)" << std::endl;
|
||||
std::cout << " │ └── GreenTriangle" << std::endl;
|
||||
std::cout << " ├── BottomLine" << std::endl;
|
||||
std::cout << " └── PurplePolygon (pentagon)" << std::endl;
|
||||
std::cout << "=============================\n" << std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 主函数
|
||||
*
|
||||
* 初始化应用程序,创建场景,运行主循环。
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
std::cout << "Extra2D Demo - Starting..." << std::endl;
|
||||
std::cout << "Extra2D Scene Graph Demo - Starting..." << std::endl;
|
||||
|
||||
AppConfig config = AppConfig::createDefault();
|
||||
config.appName = "Extra2D Demo";
|
||||
config.appName = "Extra2D Scene Graph Demo";
|
||||
config.appVersion = "1.0.0";
|
||||
|
||||
Application &app = Application::get();
|
||||
|
|
@ -37,15 +119,40 @@ int main(int argc, char *argv[]) {
|
|||
std::cout << "Application initialized successfully!" << std::endl;
|
||||
std::cout << "Window: " << app.window().width() << "x"
|
||||
<< app.window().height() << std::endl;
|
||||
std::cout << "Running main loop. Press ESC or close window to exit."
|
||||
<< std::endl;
|
||||
|
||||
auto eventService = app.events();
|
||||
if (eventService) {
|
||||
eventService->addListener(EventType::KeyPressed, [](Event &e) {
|
||||
auto &keyEvent = std::get<KeyEvent>(e.data);
|
||||
|
||||
if (keyEvent.keyCode == static_cast<int>(Key::Escape)) {
|
||||
e.handled = true;
|
||||
Application::get().quit();
|
||||
}
|
||||
});
|
||||
|
||||
eventService->addListener(EventType::MouseButtonPressed, [](Event &e) {
|
||||
auto &mouseEvent = std::get<MouseButtonEvent>(e.data);
|
||||
std::cout << "[Click] Button " << mouseEvent.button << " at ("
|
||||
<< mouseEvent.position.x << ", " << mouseEvent.position.y << ")"
|
||||
<< std::endl;
|
||||
});
|
||||
}
|
||||
|
||||
auto scene = Scene::create();
|
||||
scene->setBackgroundColor(Colors::SkyBlue);
|
||||
scene->setBackgroundColor(Color(0.12f, 0.12f, 0.16f, 1.0f));
|
||||
scene->setViewportSize(static_cast<float>(app.window().width()),
|
||||
static_cast<float>(app.window().height()));
|
||||
|
||||
createSceneGraph(scene.get());
|
||||
|
||||
app.enterScene(scene);
|
||||
|
||||
std::cout << "\nControls:" << std::endl;
|
||||
std::cout << " ESC - Exit" << std::endl;
|
||||
std::cout << " Mouse Click - Print position" << std::endl;
|
||||
std::cout << "\nRunning main loop...\n" << std::endl;
|
||||
|
||||
app.run();
|
||||
|
||||
std::cout << "Shutting down..." << std::endl;
|
||||
|
|
|
|||
Loading…
Reference in New Issue