摄像机系统,和子对象绑定
This commit is contained in:
@@ -7,6 +7,8 @@ add_library(boundard STATIC
|
|||||||
src/game.cpp
|
src/game.cpp
|
||||||
src/scene.cpp
|
src/scene.cpp
|
||||||
src/render.cpp
|
src/render.cpp
|
||||||
|
src/render_loop.cpp
|
||||||
|
src/input.cpp
|
||||||
src/config.cpp
|
src/config.cpp
|
||||||
src/engine.cpp
|
src/engine.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
"input": [
|
"input": [
|
||||||
{ "action": "quit", "keys": ["Escape"] },
|
{ "action": "quit", "keys": ["Escape"] },
|
||||||
{ "action": "switch_scene", "keys": ["Tab"] },
|
{ "action": "switch_scene", "keys": ["Tab"] },
|
||||||
{ "action": "move_left", "keys": ["Left"] },
|
{ "action": "move_left", "keys": ["a"] },
|
||||||
{ "action": "move_right", "keys": ["Right"] },
|
{ "action": "move_right", "keys": ["d"] },
|
||||||
{ "action": "move_up", "keys": ["Up"] },
|
{ "action": "move_up", "keys": ["w"] },
|
||||||
{ "action": "move_down", "keys": ["Down"] }
|
{ "action": "move_down", "keys": ["s"] }
|
||||||
],
|
],
|
||||||
|
|
||||||
"start_scene": "square",
|
"start_scene": "square",
|
||||||
|
|||||||
@@ -19,10 +19,6 @@ void register_square_behaviors() {
|
|||||||
"square_tick",
|
"square_tick",
|
||||||
[](boundard::Actor& actor, float dt, const boundard::render::InputMap& input) {
|
[](boundard::Actor& actor, float dt, const boundard::render::InputMap& input) {
|
||||||
const float t = actor.age();
|
const float t = actor.age();
|
||||||
actor.set_facing({
|
|
||||||
std::cos(t * 1.5F),
|
|
||||||
std::sin(t * 1.5F),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (actor.mesh() != nullptr) {
|
if (actor.mesh() != nullptr) {
|
||||||
actor.mesh()->set_color({
|
actor.mesh()->set_color({
|
||||||
@@ -34,7 +30,7 @@ void register_square_behaviors() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto position = actor.position();
|
auto position = actor.position();
|
||||||
const float move_speed = 0.7F;
|
constexpr float move_speed = 0.7F;
|
||||||
position[0] += input.get_axis("move_left", "move_right") * move_speed * dt;
|
position[0] += input.get_axis("move_left", "move_right") * move_speed * dt;
|
||||||
position[1] += input.get_axis("move_up", "move_down") * move_speed * dt;
|
position[1] += input.get_axis("move_up", "move_down") * move_speed * dt;
|
||||||
actor.set_position(position);
|
actor.set_position(position);
|
||||||
@@ -54,12 +50,8 @@ void register_square_behaviors() {
|
|||||||
|
|
||||||
behaviors.register_tick(
|
behaviors.register_tick(
|
||||||
"square_alt_tick",
|
"square_alt_tick",
|
||||||
[](boundard::Actor& actor, float dt, const boundard::render::InputMap& input) {
|
[](boundard::Actor& actor, const float dt, const boundard::render::InputMap& input) {
|
||||||
const float t = actor.age();
|
const float t = actor.age();
|
||||||
actor.set_facing({
|
|
||||||
std::sin(t * 1.3F),
|
|
||||||
std::cos(t * 1.3F),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (actor.mesh() != nullptr) {
|
if (actor.mesh() != nullptr) {
|
||||||
actor.mesh()->set_color({
|
actor.mesh()->set_color({
|
||||||
@@ -71,7 +63,7 @@ void register_square_behaviors() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto position = actor.position();
|
auto position = actor.position();
|
||||||
const float move_speed = 0.7F;
|
constexpr float move_speed = 0.7F;
|
||||||
position[0] += input.get_axis("move_left", "move_right") * move_speed * dt;
|
position[0] += input.get_axis("move_left", "move_right") * move_speed * dt;
|
||||||
position[1] += input.get_axis("move_up", "move_down") * move_speed * dt;
|
position[1] += input.get_axis("move_up", "move_down") * move_speed * dt;
|
||||||
actor.set_position(position);
|
actor.set_position(position);
|
||||||
|
|||||||
@@ -1,21 +1,31 @@
|
|||||||
{
|
{
|
||||||
"actors": [
|
"default_camera": "square_camera",
|
||||||
{
|
"grid_background": true,
|
||||||
"name": "square",
|
"actors": [
|
||||||
"init": "square_init",
|
{
|
||||||
"tick": "square_tick",
|
"name": "square",
|
||||||
"position": {
|
"init": "square_init",
|
||||||
"x": 0,
|
"tick": "square_tick",
|
||||||
"y": 0
|
"position": {
|
||||||
},
|
"x": 0,
|
||||||
"facing": {
|
"y": 0
|
||||||
"x": 1,
|
},
|
||||||
"y": 0
|
"facing": {
|
||||||
},
|
"x": 1,
|
||||||
"mesh": {
|
"y": 0
|
||||||
"shape": "quad",
|
},
|
||||||
"texture": "Dank.png"
|
"mesh": {
|
||||||
}
|
"shape": "quad",
|
||||||
}
|
"texture": "Dank.png"
|
||||||
]
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"name": "square_camera",
|
||||||
|
"type": "camera",
|
||||||
|
"position": { "x": 0, "y": 0 },
|
||||||
|
"zoom": 0.2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"default_camera": "square_alt_camera",
|
||||||
|
"grid_background": true,
|
||||||
"actors": [
|
"actors": [
|
||||||
{
|
{
|
||||||
"name": "square_alt",
|
"name": "square_alt",
|
||||||
@@ -9,7 +11,15 @@
|
|||||||
"mesh": {
|
"mesh": {
|
||||||
"shape": "quad",
|
"shape": "quad",
|
||||||
"color": { "r": 0.9, "g": 0.3, "b": 0.2, "a": 1.0 }
|
"color": { "r": 0.9, "g": 0.3, "b": 0.2, "a": 1.0 }
|
||||||
}
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"name": "square_alt_camera",
|
||||||
|
"type": "camera",
|
||||||
|
"position": { "x": 0, "y": 0 },
|
||||||
|
"zoom": 1.0
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-9
@@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <game.hpp>
|
#include <game.hpp>
|
||||||
#include <mesh.hpp>
|
#include <mesh.hpp>
|
||||||
@@ -15,14 +16,16 @@ class InputMap;
|
|||||||
|
|
||||||
namespace boundard {
|
namespace boundard {
|
||||||
|
|
||||||
class Actor {
|
class Actor : public std::enable_shared_from_this<Actor> {
|
||||||
private:
|
private:
|
||||||
size_t id;
|
size_t id;
|
||||||
std::string nickName_;
|
std::string nickName_;
|
||||||
mutable std::mutex transform_mutex_;
|
mutable std::mutex transform_mutex_;
|
||||||
std::array<float, 2> position_{0.0F, 0.0F};
|
std::array<float, 2> position_{0.0F, 0.0F};
|
||||||
std::array<float, 2> facing_{1.0F, 0.0F};
|
std::array<float, 2> facing_{1.0F, 0.0F};
|
||||||
std::shared_ptr<Mesh> mesh_;
|
mutable std::mutex tree_mutex_;
|
||||||
|
std::weak_ptr<Actor> parent_;
|
||||||
|
std::vector<std::shared_ptr<Actor>> children_;
|
||||||
|
|
||||||
float age_ = 0.0F;
|
float age_ = 0.0F;
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
@@ -43,11 +46,6 @@ class Actor {
|
|||||||
nickName_ = std::move(nickName);
|
nickName_ = std::move(nickName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Actor(std::string nickName, Game& game, std::shared_ptr<Mesh> mesh)
|
|
||||||
: Actor(std::move(nickName), game) {
|
|
||||||
mesh_ = std::move(mesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Actor& getInstance() {
|
static Actor& getInstance() {
|
||||||
static Actor instance;
|
static Actor instance;
|
||||||
return instance;
|
return instance;
|
||||||
@@ -75,6 +73,9 @@ class Actor {
|
|||||||
if (init_handler_) {
|
if (init_handler_) {
|
||||||
init_handler_(*this);
|
init_handler_(*this);
|
||||||
}
|
}
|
||||||
|
for (const std::shared_ptr<Actor>& child : children()) {
|
||||||
|
child->initialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 类似 Godot 的 _process(delta)。
|
// 类似 Godot 的 _process(delta)。
|
||||||
@@ -83,6 +84,9 @@ class Actor {
|
|||||||
if (tick_handler_) {
|
if (tick_handler_) {
|
||||||
tick_handler_(*this, dt, input);
|
tick_handler_(*this, dt, input);
|
||||||
}
|
}
|
||||||
|
for (const std::shared_ptr<Actor>& child : children()) {
|
||||||
|
child->process(dt, input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<float, 2> position() const {
|
std::array<float, 2> position() const {
|
||||||
@@ -95,6 +99,19 @@ class Actor {
|
|||||||
position_ = position;
|
position_ = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::array<float, 2> global_position() const {
|
||||||
|
const std::array<float, 2> local_position = position();
|
||||||
|
const std::shared_ptr<Actor> parent = this->parent();
|
||||||
|
if (parent == nullptr) return local_position;
|
||||||
|
|
||||||
|
const std::array<float, 2> parent_position = parent->global_position();
|
||||||
|
const std::array<float, 2> parent_facing = parent->global_facing();
|
||||||
|
return {parent_position[0] + parent_facing[0] * local_position[0] -
|
||||||
|
parent_facing[1] * local_position[1],
|
||||||
|
parent_position[1] + parent_facing[1] * local_position[0] +
|
||||||
|
parent_facing[0] * local_position[1]};
|
||||||
|
}
|
||||||
|
|
||||||
std::array<float, 2> facing() const {
|
std::array<float, 2> facing() const {
|
||||||
std::lock_guard<std::mutex> lock(transform_mutex_);
|
std::lock_guard<std::mutex> lock(transform_mutex_);
|
||||||
return facing_;
|
return facing_;
|
||||||
@@ -105,8 +122,67 @@ class Actor {
|
|||||||
facing_ = facing;
|
facing_ = facing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::shared_ptr<Mesh>& mesh() const { return mesh_; }
|
std::array<float, 2> global_facing() const {
|
||||||
void set_mesh(std::shared_ptr<Mesh> mesh) { mesh_ = std::move(mesh); }
|
std::array<float, 2> local_facing = facing();
|
||||||
|
const std::shared_ptr<Actor> parent = this->parent();
|
||||||
|
if (parent == nullptr) return local_facing;
|
||||||
|
|
||||||
|
const std::array<float, 2> parent_facing = parent->global_facing();
|
||||||
|
return {parent_facing[0] * local_facing[0] - parent_facing[1] * local_facing[1],
|
||||||
|
parent_facing[1] * local_facing[0] + parent_facing[0] * local_facing[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const std::shared_ptr<Mesh>& mesh() const {
|
||||||
|
static const std::shared_ptr<Mesh> no_mesh;
|
||||||
|
return no_mesh;
|
||||||
|
}
|
||||||
|
virtual void set_mesh(std::shared_ptr<Mesh>) {}
|
||||||
|
|
||||||
|
std::shared_ptr<Actor> parent() const {
|
||||||
|
std::lock_guard<std::mutex> lock(tree_mutex_);
|
||||||
|
return parent_.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Actor>> children() const {
|
||||||
|
std::lock_guard<std::mutex> lock(tree_mutex_);
|
||||||
|
return children_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool add_child(const std::shared_ptr<Actor>& child) {
|
||||||
|
if (child == nullptr) return false;
|
||||||
|
const std::shared_ptr<Actor> self = shared_from_this();
|
||||||
|
if (child == self) return false;
|
||||||
|
for (std::shared_ptr<Actor> ancestor = self; ancestor != nullptr;
|
||||||
|
ancestor = ancestor->parent()) {
|
||||||
|
if (ancestor == child) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const std::shared_ptr<Actor> old_parent = child->parent()) {
|
||||||
|
old_parent->remove_child(child);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(tree_mutex_);
|
||||||
|
children_.push_back(child);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(child->tree_mutex_);
|
||||||
|
child->parent_ = self;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove_child(const std::shared_ptr<Actor>& child) {
|
||||||
|
if (child == nullptr) return false;
|
||||||
|
std::lock_guard<std::mutex> lock(tree_mutex_);
|
||||||
|
for (auto it = children_.begin(); it != children_.end(); ++it) {
|
||||||
|
if (*it != child) continue;
|
||||||
|
children_.erase(it);
|
||||||
|
std::lock_guard<std::mutex> child_lock(child->tree_mutex_);
|
||||||
|
child->parent_.reset();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace boundard
|
} // namespace boundard
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include <game.hpp>
|
#include <game.hpp>
|
||||||
#include <mesh.hpp>
|
#include <mesh.hpp>
|
||||||
#include <actor.hpp>
|
#include <actor.hpp>
|
||||||
|
#include <sprite_actor.hpp>
|
||||||
|
#include <camera.hpp>
|
||||||
#include <behavior.hpp>
|
#include <behavior.hpp>
|
||||||
#include <render.hpp>
|
#include <render.hpp>
|
||||||
#include <config.hpp>
|
#include <config.hpp>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <actor.hpp>
|
||||||
|
|
||||||
|
namespace boundard {
|
||||||
|
|
||||||
|
class Camera : public Actor {
|
||||||
|
public:
|
||||||
|
Camera(std::string name, Game& game) : Actor(std::move(name), game) {}
|
||||||
|
|
||||||
|
float zoom() const { return zoom_; }
|
||||||
|
void set_zoom(float zoom) { zoom_ = zoom > 0.0F ? zoom : 0.0001F; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
float zoom_ = 1.0F;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace boundard
|
||||||
@@ -21,11 +21,14 @@ struct MeshConfig {
|
|||||||
|
|
||||||
struct ActorConfig {
|
struct ActorConfig {
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string type = "actor";
|
||||||
std::string init_function;
|
std::string init_function;
|
||||||
std::string tick_function;
|
std::string tick_function;
|
||||||
std::array<float, 2> position{0.0F, 0.0F};
|
std::array<float, 2> position{0.0F, 0.0F};
|
||||||
std::array<float, 2> facing{1.0F, 0.0F};
|
std::array<float, 2> facing{1.0F, 0.0F};
|
||||||
|
float zoom = 1.0F;
|
||||||
MeshConfig mesh;
|
MeshConfig mesh;
|
||||||
|
std::vector<ActorConfig> children;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InputBindingConfig {
|
struct InputBindingConfig {
|
||||||
@@ -38,6 +41,8 @@ struct SceneConfig {
|
|||||||
std::string name;
|
std::string name;
|
||||||
std::string path;
|
std::string path;
|
||||||
std::vector<ActorConfig> actors;
|
std::vector<ActorConfig> actors;
|
||||||
|
std::string default_camera;
|
||||||
|
bool grid_background = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GameConfig {
|
struct GameConfig {
|
||||||
|
|||||||
@@ -44,6 +44,33 @@ class Mesh {
|
|||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Mesh grid(int half_cells = 12) {
|
||||||
|
Mesh mesh;
|
||||||
|
const float world_to_local = 1.0F / 0.35F;
|
||||||
|
const float half_extent = static_cast<float>(half_cells) * world_to_local;
|
||||||
|
const float line_half_width = 0.012F * world_to_local;
|
||||||
|
const std::array<float, 4> line_color{1.0F, 1.0F, 1.0F, 1.0F};
|
||||||
|
auto add_rect = [&](float left, float bottom, float right, float top) {
|
||||||
|
const uint32_t base = static_cast<uint32_t>(mesh.vertices.size());
|
||||||
|
mesh.vertices.insert(mesh.vertices.end(), {
|
||||||
|
{{left, bottom}, line_color, {0.0F, 0.0F}},
|
||||||
|
{{right, bottom}, line_color, {1.0F, 0.0F}},
|
||||||
|
{{right, top}, line_color, {1.0F, 1.0F}},
|
||||||
|
{{left, top}, line_color, {0.0F, 1.0F}},
|
||||||
|
});
|
||||||
|
mesh.indices.insert(mesh.indices.end(),
|
||||||
|
{base, base + 1, base + 2, base + 2, base + 3, base});
|
||||||
|
};
|
||||||
|
for (int cell = -half_cells; cell <= half_cells; ++cell) {
|
||||||
|
const float coordinate = static_cast<float>(cell) * world_to_local;
|
||||||
|
add_rect(coordinate - line_half_width, -half_extent, coordinate + line_half_width,
|
||||||
|
half_extent);
|
||||||
|
add_rect(-half_extent, coordinate - line_half_width, half_extent,
|
||||||
|
coordinate + line_half_width);
|
||||||
|
}
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<std::mutex> color_mutex_ = std::make_unique<std::mutex>();
|
std::unique_ptr<std::mutex> color_mutex_ = std::make_unique<std::mutex>();
|
||||||
std::array<float, 4> color_{1.0F, 1.0F, 1.0F, 1.0F};
|
std::array<float, 4> color_{1.0F, 1.0F, 1.0F, 1.0F};
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
|
|
||||||
#include <actor.hpp>
|
#include <actor.hpp>
|
||||||
|
|
||||||
|
namespace boundard {
|
||||||
|
class Camera;
|
||||||
|
}
|
||||||
|
|
||||||
namespace boundard::render {
|
namespace boundard::render {
|
||||||
|
|
||||||
struct InputEvent {
|
struct InputEvent {
|
||||||
@@ -51,6 +55,7 @@ class Renderer {
|
|||||||
|
|
||||||
void init(int width, int height, const char* title);
|
void init(int width, int height, const char* title);
|
||||||
void draw(const Actor* const* actors, size_t actor_count);
|
void draw(const Actor* const* actors, size_t actor_count);
|
||||||
|
void draw(const Actor* const* actors, size_t actor_count, const Camera* camera);
|
||||||
bool should_close() const;
|
bool should_close() const;
|
||||||
void poll_events() const;
|
void poll_events() const;
|
||||||
|
|
||||||
@@ -119,6 +124,7 @@ class RenderLoop {
|
|||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
void set_actors(std::vector<Actor*> actors);
|
void set_actors(std::vector<Actor*> actors);
|
||||||
|
void set_camera(Camera* camera);
|
||||||
void poll_events() const;
|
void poll_events() const;
|
||||||
bool should_close() const;
|
bool should_close() const;
|
||||||
|
|
||||||
@@ -134,6 +140,7 @@ class RenderLoop {
|
|||||||
std::atomic<bool> running_{false};
|
std::atomic<bool> running_{false};
|
||||||
mutable std::mutex actors_mutex_;
|
mutable std::mutex actors_mutex_;
|
||||||
std::vector<Actor*> actors_;
|
std::vector<Actor*> actors_;
|
||||||
|
Camera* camera_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace boundard::render
|
} // namespace boundard::render
|
||||||
|
|||||||
+6
-1
@@ -11,6 +11,7 @@ class InputMap;
|
|||||||
namespace boundard {
|
namespace boundard {
|
||||||
|
|
||||||
class Actor;
|
class Actor;
|
||||||
|
class Camera;
|
||||||
|
|
||||||
// 运行时场景:持有场景名和其中的 Actor,并统一驱动 Actor 的 init/tick。
|
// 运行时场景:持有场景名和其中的 Actor,并统一驱动 Actor 的 init/tick。
|
||||||
class Scene {
|
class Scene {
|
||||||
@@ -30,6 +31,9 @@ class Scene {
|
|||||||
void set_actors(std::vector<std::shared_ptr<Actor>> actors);
|
void set_actors(std::vector<std::shared_ptr<Actor>> actors);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
void set_default_camera(std::string name);
|
||||||
|
Camera* primary_camera() const;
|
||||||
|
|
||||||
const std::vector<Actor*>& actors() const;
|
const std::vector<Actor*>& actors() const;
|
||||||
std::vector<Actor*>& actors();
|
std::vector<Actor*>& actors();
|
||||||
|
|
||||||
@@ -39,7 +43,8 @@ class Scene {
|
|||||||
private:
|
private:
|
||||||
std::string name_;
|
std::string name_;
|
||||||
std::vector<std::shared_ptr<Actor>> actor_storage_;
|
std::vector<std::shared_ptr<Actor>> actor_storage_;
|
||||||
std::vector<Actor*> actors_;
|
mutable std::vector<Actor*> actors_;
|
||||||
|
std::string default_camera_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace boundard
|
} // namespace boundard
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <actor.hpp>
|
||||||
|
|
||||||
|
namespace boundard {
|
||||||
|
|
||||||
|
class SpriteActor : public Actor {
|
||||||
|
public:
|
||||||
|
SpriteActor(std::string name, Game& game, std::shared_ptr<Mesh> mesh)
|
||||||
|
: Actor(std::move(name), game), mesh_(std::move(mesh)) {}
|
||||||
|
|
||||||
|
const std::shared_ptr<Mesh>& mesh() const override { return mesh_; }
|
||||||
|
void set_mesh(std::shared_ptr<Mesh> mesh) override { mesh_ = std::move(mesh); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Mesh> mesh_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace boundard
|
||||||
+12
-1
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"default_camera": "main_camera",
|
||||||
|
"grid_background": true,
|
||||||
"actors": [
|
"actors": [
|
||||||
{
|
{
|
||||||
"name": "square",
|
"name": "square",
|
||||||
@@ -13,7 +15,16 @@
|
|||||||
"mesh": {
|
"mesh": {
|
||||||
"shape": "quad",
|
"shape": "quad",
|
||||||
"texture": "Dank.png"
|
"texture": "Dank.png"
|
||||||
}
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"name": "main_camera",
|
||||||
|
"type": "camera",
|
||||||
|
"position": { "x": 0, "y": 0 },
|
||||||
|
"facing": { "x": 1, "y": 0 },
|
||||||
|
"zoom": 1.0
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-4
@@ -243,6 +243,9 @@ ActorConfig parse_actor(const Json::Value& value) {
|
|||||||
if (value.isMember("name")) {
|
if (value.isMember("name")) {
|
||||||
actor.name = json_string(value["name"], "");
|
actor.name = json_string(value["name"], "");
|
||||||
}
|
}
|
||||||
|
if (value.isMember("type")) {
|
||||||
|
actor.type = json_string(value["type"], actor.type);
|
||||||
|
}
|
||||||
if (value.isMember("init")) {
|
if (value.isMember("init")) {
|
||||||
actor.init_function = json_string(value["init"], "");
|
actor.init_function = json_string(value["init"], "");
|
||||||
}
|
}
|
||||||
@@ -261,6 +264,9 @@ ActorConfig parse_actor(const Json::Value& value) {
|
|||||||
actor.facing[0] = json_float(facing["x"], actor.facing[0]);
|
actor.facing[0] = json_float(facing["x"], actor.facing[0]);
|
||||||
actor.facing[1] = json_float(facing["y"], actor.facing[1]);
|
actor.facing[1] = json_float(facing["y"], actor.facing[1]);
|
||||||
}
|
}
|
||||||
|
if (value.isMember("zoom")) {
|
||||||
|
actor.zoom = json_float(value["zoom"], actor.zoom);
|
||||||
|
}
|
||||||
|
|
||||||
if (value.isMember("mesh") && value["mesh"].isObject()) {
|
if (value.isMember("mesh") && value["mesh"].isObject()) {
|
||||||
const Json::Value& mesh = value["mesh"];
|
const Json::Value& mesh = value["mesh"];
|
||||||
@@ -272,6 +278,11 @@ ActorConfig parse_actor(const Json::Value& value) {
|
|||||||
}
|
}
|
||||||
parse_color(mesh, actor.mesh);
|
parse_color(mesh, actor.mesh);
|
||||||
}
|
}
|
||||||
|
if (value.isMember("children") && value["children"].isArray()) {
|
||||||
|
for (const Json::Value& child : value["children"]) {
|
||||||
|
if (child.isObject()) actor.children.push_back(parse_actor(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
@@ -312,11 +323,13 @@ void extract_actors(const Json::Value& root, std::vector<ActorConfig>& actors) {
|
|||||||
void resolve_texture_paths(std::vector<ActorConfig>& actors,
|
void resolve_texture_paths(std::vector<ActorConfig>& actors,
|
||||||
const std::filesystem::path& base_dir) {
|
const std::filesystem::path& base_dir) {
|
||||||
for (ActorConfig& actor : actors) {
|
for (ActorConfig& actor : actors) {
|
||||||
if (actor.mesh.texture.empty()) continue;
|
if (!actor.mesh.texture.empty()) {
|
||||||
std::filesystem::path texture_path(actor.mesh.texture);
|
std::filesystem::path texture_path(actor.mesh.texture);
|
||||||
if (texture_path.is_relative()) {
|
if (texture_path.is_relative()) {
|
||||||
actor.mesh.texture = (base_dir / texture_path).lexically_normal().string();
|
actor.mesh.texture = (base_dir / texture_path).lexically_normal().string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
resolve_texture_paths(actor.children, base_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,6 +343,14 @@ bool parse_scene_entry(const Json::Value& item, SceneConfig& scene,
|
|||||||
if (item.isMember("name")) {
|
if (item.isMember("name")) {
|
||||||
scene.name = json_string(item["name"], "");
|
scene.name = json_string(item["name"], "");
|
||||||
}
|
}
|
||||||
|
if (item.isMember("default_camera")) {
|
||||||
|
scene.default_camera = json_string(item["default_camera"], "");
|
||||||
|
} else if (item.isMember("camera")) {
|
||||||
|
scene.default_camera = json_string(item["camera"], "");
|
||||||
|
}
|
||||||
|
if (item.isMember("grid_background")) {
|
||||||
|
scene.grid_background = item["grid_background"].asBool();
|
||||||
|
}
|
||||||
if (scene.name.empty()) {
|
if (scene.name.empty()) {
|
||||||
error = "场景缺少 name";
|
error = "场景缺少 name";
|
||||||
return false;
|
return false;
|
||||||
@@ -353,6 +374,14 @@ bool parse_scene_entry(const Json::Value& item, SceneConfig& scene,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
extract_actors(root, scene.actors);
|
extract_actors(root, scene.actors);
|
||||||
|
if (root.isMember("default_camera")) {
|
||||||
|
scene.default_camera = json_string(root["default_camera"], scene.default_camera);
|
||||||
|
} else if (root.isMember("camera")) {
|
||||||
|
scene.default_camera = json_string(root["camera"], scene.default_camera);
|
||||||
|
}
|
||||||
|
if (root.isMember("grid_background")) {
|
||||||
|
scene.grid_background = root["grid_background"].asBool();
|
||||||
|
}
|
||||||
resolve_texture_paths(scene.actors, scene_path.parent_path());
|
resolve_texture_paths(scene.actors, scene_path.parent_path());
|
||||||
} else {
|
} else {
|
||||||
// 允许场景直接内联在游戏配置中。
|
// 允许场景直接内联在游戏配置中。
|
||||||
|
|||||||
+24
-1
@@ -8,6 +8,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boundard.hpp>
|
#include <boundard.hpp>
|
||||||
|
#include <camera.hpp>
|
||||||
|
#include <sprite_actor.hpp>
|
||||||
|
|
||||||
namespace boundard {
|
namespace boundard {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -24,9 +26,21 @@ std::shared_ptr<Actor> create_actor(const ActorConfig& actor_config, Game& game,
|
|||||||
mesh->set_color(actor_config.mesh.color);
|
mesh->set_color(actor_config.mesh.color);
|
||||||
mesh->texture = actor_config.mesh.texture;
|
mesh->texture = actor_config.mesh.texture;
|
||||||
|
|
||||||
auto actor = std::make_shared<Actor>(actor_config.name, game, mesh);
|
std::shared_ptr<Actor> actor;
|
||||||
|
if (actor_config.type == "camera") {
|
||||||
|
actor = std::make_shared<Camera>(actor_config.name, game);
|
||||||
|
} else {
|
||||||
|
actor = std::make_shared<SpriteActor>(actor_config.name, game, mesh);
|
||||||
|
}
|
||||||
actor->set_position(actor_config.position);
|
actor->set_position(actor_config.position);
|
||||||
actor->set_facing(actor_config.facing);
|
actor->set_facing(actor_config.facing);
|
||||||
|
if (auto* camera = dynamic_cast<Camera*>(actor.get())) {
|
||||||
|
camera->set_zoom(actor_config.zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ActorConfig& child_config : actor_config.children) {
|
||||||
|
actor->add_child(create_actor(child_config, game, behaviors));
|
||||||
|
}
|
||||||
|
|
||||||
if (!actor_config.init_function.empty()) {
|
if (!actor_config.init_function.empty()) {
|
||||||
auto handler = behaviors.init(actor_config.init_function);
|
auto handler = behaviors.init(actor_config.init_function);
|
||||||
@@ -55,10 +69,18 @@ std::shared_ptr<Scene> build_scene(const SceneConfig& scene, Game& game,
|
|||||||
BehaviorRegistry& behaviors) {
|
BehaviorRegistry& behaviors) {
|
||||||
auto runtime_scene = std::make_shared<Scene>(scene.name);
|
auto runtime_scene = std::make_shared<Scene>(scene.name);
|
||||||
|
|
||||||
|
if (scene.grid_background) {
|
||||||
|
auto grid = std::make_shared<SpriteActor>("__grid", game,
|
||||||
|
std::make_shared<Mesh>(Mesh::grid()));
|
||||||
|
grid->mesh()->set_color({0.20F, 0.28F, 0.36F, 0.75F});
|
||||||
|
runtime_scene->add_actor(std::move(grid));
|
||||||
|
}
|
||||||
|
|
||||||
for (const ActorConfig& actor_config : scene.actors) {
|
for (const ActorConfig& actor_config : scene.actors) {
|
||||||
auto actor = create_actor(actor_config, game, behaviors);
|
auto actor = create_actor(actor_config, game, behaviors);
|
||||||
runtime_scene->add_actor(std::move(actor));
|
runtime_scene->add_actor(std::move(actor));
|
||||||
}
|
}
|
||||||
|
runtime_scene->set_default_camera(scene.default_camera);
|
||||||
|
|
||||||
return runtime_scene;
|
return runtime_scene;
|
||||||
}
|
}
|
||||||
@@ -143,6 +165,7 @@ int run_engine(int argc, char** argv, const char* default_config_path) {
|
|||||||
scene->initialize();
|
scene->initialize();
|
||||||
game.set_current_scene(scene);
|
game.set_current_scene(scene);
|
||||||
render_loop.set_actors(scene->actors());
|
render_loop.set_actors(scene->actors());
|
||||||
|
render_loop.set_camera(scene->primary_camera());
|
||||||
};
|
};
|
||||||
|
|
||||||
if (active_scene != nullptr) {
|
if (active_scene != nullptr) {
|
||||||
|
|||||||
+139
@@ -0,0 +1,139 @@
|
|||||||
|
#include <render.hpp>
|
||||||
|
|
||||||
|
namespace boundard::render {
|
||||||
|
|
||||||
|
void InputMap::add_action(const std::string& action) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
actions_.try_emplace(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputMap::bind_key(const std::string& action, int key) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
ActionState& state = actions_[action];
|
||||||
|
state.keys.insert(key);
|
||||||
|
state.held_keys.try_emplace(key, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputMap::unbind_key(const std::string& action, int key) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto action_it = actions_.find(action);
|
||||||
|
if (action_it == actions_.end()) return;
|
||||||
|
ActionState& state = action_it->second;
|
||||||
|
state.keys.erase(key);
|
||||||
|
auto held_it = state.held_keys.find(key);
|
||||||
|
if (held_it != state.held_keys.end() && held_it->second) {
|
||||||
|
held_it->second = false;
|
||||||
|
if (state.held_key_count > 0) --state.held_key_count;
|
||||||
|
if (state.held_key_count + state.held_button_count == 0) {
|
||||||
|
state.pressed = false;
|
||||||
|
state.just_released = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.held_keys.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputMap::bind_mouse_button(const std::string& action, int button) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
ActionState& state = actions_[action];
|
||||||
|
state.mouse_buttons.insert(button);
|
||||||
|
state.held_buttons.try_emplace(button, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputMap::unbind_mouse_button(const std::string& action, int button) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto action_it = actions_.find(action);
|
||||||
|
if (action_it == actions_.end()) return;
|
||||||
|
ActionState& state = action_it->second;
|
||||||
|
state.mouse_buttons.erase(button);
|
||||||
|
auto held_it = state.held_buttons.find(button);
|
||||||
|
if (held_it != state.held_buttons.end() && held_it->second) {
|
||||||
|
held_it->second = false;
|
||||||
|
if (state.held_button_count > 0) --state.held_button_count;
|
||||||
|
if (state.held_key_count + state.held_button_count == 0) {
|
||||||
|
state.pressed = false;
|
||||||
|
state.just_released = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.held_buttons.erase(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputMap::handle_event(const InputEvent& event) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
for (auto& [action, state] : actions_) {
|
||||||
|
std::unordered_set<int>* bindings = nullptr;
|
||||||
|
std::unordered_map<int, bool>* held_map = nullptr;
|
||||||
|
size_t* held_count = nullptr;
|
||||||
|
if (event.type == InputEvent::Type::Key) {
|
||||||
|
bindings = &state.keys;
|
||||||
|
held_map = &state.held_keys;
|
||||||
|
held_count = &state.held_key_count;
|
||||||
|
} else if (event.type == InputEvent::Type::MouseButton) {
|
||||||
|
bindings = &state.mouse_buttons;
|
||||||
|
held_map = &state.held_buttons;
|
||||||
|
held_count = &state.held_button_count;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const int input_id = event.type == InputEvent::Type::Key ? event.key : event.button;
|
||||||
|
if (bindings->find(input_id) == bindings->end()) continue;
|
||||||
|
if (event.action == InputEvent::Action::Press) {
|
||||||
|
bool& held = (*held_map)[input_id];
|
||||||
|
if (!held) {
|
||||||
|
held = true;
|
||||||
|
++(*held_count);
|
||||||
|
if (state.held_key_count + state.held_button_count == 1) {
|
||||||
|
state.pressed = true;
|
||||||
|
state.just_pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event.action == InputEvent::Action::Release) {
|
||||||
|
auto held_it = held_map->find(input_id);
|
||||||
|
if (held_it == held_map->end() || !held_it->second) continue;
|
||||||
|
held_it->second = false;
|
||||||
|
if (*held_count > 0) --(*held_count);
|
||||||
|
if (state.held_key_count + state.held_button_count == 0) {
|
||||||
|
state.pressed = false;
|
||||||
|
state.just_released = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputMap::end_frame() {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
for (auto& [action, state] : actions_) {
|
||||||
|
state.just_pressed = false;
|
||||||
|
state.just_released = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputMap::is_action_pressed(const std::string& action) const {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto it = actions_.find(action);
|
||||||
|
return it != actions_.end() && it->second.pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputMap::is_action_just_pressed(const std::string& action) const {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto it = actions_.find(action);
|
||||||
|
return it != actions_.end() && it->second.just_pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputMap::is_action_just_released(const std::string& action) const {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto it = actions_.find(action);
|
||||||
|
return it != actions_.end() && it->second.just_released;
|
||||||
|
}
|
||||||
|
|
||||||
|
float InputMap::get_action_strength(const std::string& action) const {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
auto it = actions_.find(action);
|
||||||
|
return it != actions_.end() && it->second.pressed ? 1.0F : 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
float InputMap::get_axis(const std::string& negative_action,
|
||||||
|
const std::string& positive_action) const {
|
||||||
|
return get_action_strength(positive_action) - get_action_strength(negative_action);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace boundard::render
|
||||||
+194
-79
@@ -1,4 +1,5 @@
|
|||||||
#include <render.hpp>
|
#include <render.hpp>
|
||||||
|
#include <camera.hpp>
|
||||||
|
|
||||||
#define GLFW_INCLUDE_VULKAN
|
#define GLFW_INCLUDE_VULKAN
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
@@ -197,6 +198,7 @@ struct Renderer::Impl {
|
|||||||
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
||||||
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
|
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
|
||||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||||
|
std::atomic<bool> framebuffer_resized{false};
|
||||||
|
|
||||||
VkCommandPool command_pool = VK_NULL_HANDLE;
|
VkCommandPool command_pool = VK_NULL_HANDLE;
|
||||||
std::vector<FrameData> frames;
|
std::vector<FrameData> frames;
|
||||||
@@ -221,11 +223,12 @@ struct Renderer::Impl {
|
|||||||
Renderer::InputCallback input_callback;
|
Renderer::InputCallback input_callback;
|
||||||
|
|
||||||
void init(int width, int height, const char* title);
|
void init(int width, int height, const char* title);
|
||||||
void draw(const Actor* const* actors, size_t actor_count);
|
void draw(const Actor* const* actors, size_t actor_count, const Camera* camera);
|
||||||
void upload_mesh(const Mesh& mesh);
|
void upload_mesh(const Mesh& mesh);
|
||||||
void create_texture(MeshResource& resource, const std::string& path);
|
void create_texture(MeshResource& resource, const std::string& path);
|
||||||
VkCommandBuffer begin_transfer();
|
VkCommandBuffer begin_transfer();
|
||||||
void end_transfer(VkCommandBuffer command_buffer);
|
void end_transfer(VkCommandBuffer command_buffer);
|
||||||
|
void recreate_swapchain();
|
||||||
void destroy();
|
void destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -240,7 +243,11 @@ void Renderer::init(int width, int height, const char* title) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::draw(const Actor* const* actors, size_t actor_count) {
|
void Renderer::draw(const Actor* const* actors, size_t actor_count) {
|
||||||
impl_->draw(actors, actor_count);
|
impl_->draw(actors, actor_count, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::draw(const Actor* const* actors, size_t actor_count, const Camera* camera) {
|
||||||
|
impl_->draw(actors, actor_count, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Renderer::should_close() const {
|
bool Renderer::should_close() const {
|
||||||
@@ -277,6 +284,7 @@ void Renderer::destroy() {
|
|||||||
impl_->destroy();
|
impl_->destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0 // InputMap implementation moved to input.cpp.
|
||||||
void InputMap::add_action(const std::string& action) {
|
void InputMap::add_action(const std::string& action) {
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
actions_.try_emplace(action);
|
actions_.try_emplace(action);
|
||||||
@@ -428,72 +436,7 @@ float InputMap::get_axis(const std::string& negative_action,
|
|||||||
const std::string& positive_action) const {
|
const std::string& positive_action) const {
|
||||||
return get_action_strength(positive_action) - get_action_strength(negative_action);
|
return get_action_strength(positive_action) - get_action_strength(negative_action);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
RenderLoop::~RenderLoop() {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderLoop::start(int width, int height, const char* title) {
|
|
||||||
if (running_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer_.init(width, height, title);
|
|
||||||
renderer_.set_input_callback([this](const InputEvent& event) {
|
|
||||||
input_map_.handle_event(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
running_ = true;
|
|
||||||
thread_ = std::thread(&RenderLoop::run, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderLoop::stop() {
|
|
||||||
if (!running_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
running_ = false;
|
|
||||||
if (thread_.joinable()) {
|
|
||||||
thread_.join();
|
|
||||||
}
|
|
||||||
renderer_.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderLoop::set_actors(std::vector<Actor*> actors) {
|
|
||||||
std::lock_guard<std::mutex> lock(actors_mutex_);
|
|
||||||
actors_ = std::move(actors);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderLoop::poll_events() const {
|
|
||||||
renderer_.poll_events();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RenderLoop::should_close() const {
|
|
||||||
return renderer_.should_close();
|
|
||||||
}
|
|
||||||
|
|
||||||
InputMap& RenderLoop::input() {
|
|
||||||
return input_map_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const InputMap& RenderLoop::input() const {
|
|
||||||
return input_map_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderLoop::run() {
|
|
||||||
while (running_) {
|
|
||||||
std::vector<Actor*> actors;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(actors_mutex_);
|
|
||||||
actors = actors_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!actors.empty()) {
|
|
||||||
renderer_.draw(actors.data(), actors.size());
|
|
||||||
} else {
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Renderer::Impl::init(int width, int height, const char* title) {
|
void Renderer::Impl::init(int width, int height, const char* title) {
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
@@ -509,8 +452,11 @@ void Renderer::Impl::init(int width, int height, const char* title) {
|
|||||||
if (window == nullptr) {
|
if (window == nullptr) {
|
||||||
throw std::runtime_error("failed to create GLFW window");
|
throw std::runtime_error("failed to create GLFW window");
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwSetWindowUserPointer(window, this);
|
glfwSetWindowUserPointer(window, this);
|
||||||
|
glfwSetFramebufferSizeCallback(window, [](GLFWwindow* target, int, int) {
|
||||||
|
auto* impl = static_cast<Renderer::Impl*>(glfwGetWindowUserPointer(target));
|
||||||
|
if (impl != nullptr) impl->framebuffer_resized = true;
|
||||||
|
});
|
||||||
glfwSetKeyCallback(window, [](GLFWwindow* target, int key, int scancode, int action,
|
glfwSetKeyCallback(window, [](GLFWwindow* target, int key, int scancode, int action,
|
||||||
int mods) {
|
int mods) {
|
||||||
auto* impl = static_cast<Renderer::Impl*>(glfwGetWindowUserPointer(target));
|
auto* impl = static_cast<Renderer::Impl*>(glfwGetWindowUserPointer(target));
|
||||||
@@ -1051,7 +997,15 @@ void Renderer::Impl::init(int width, int height, const char* title) {
|
|||||||
pipeline_info.pMultisampleState = &multisampling;
|
pipeline_info.pMultisampleState = &multisampling;
|
||||||
pipeline_info.pDepthStencilState = nullptr;
|
pipeline_info.pDepthStencilState = nullptr;
|
||||||
pipeline_info.pColorBlendState = &color_blending;
|
pipeline_info.pColorBlendState = &color_blending;
|
||||||
pipeline_info.pDynamicState = nullptr;
|
const std::array<VkDynamicState, 2> dynamic_states = {
|
||||||
|
VK_DYNAMIC_STATE_VIEWPORT,
|
||||||
|
VK_DYNAMIC_STATE_SCISSOR,
|
||||||
|
};
|
||||||
|
VkPipelineDynamicStateCreateInfo dynamic_state{};
|
||||||
|
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||||
|
dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
|
||||||
|
dynamic_state.pDynamicStates = dynamic_states.data();
|
||||||
|
pipeline_info.pDynamicState = &dynamic_state;
|
||||||
pipeline_info.layout = pipeline_layout;
|
pipeline_info.layout = pipeline_layout;
|
||||||
pipeline_info.renderPass = render_pass;
|
pipeline_info.renderPass = render_pass;
|
||||||
pipeline_info.subpass = 0;
|
pipeline_info.subpass = 0;
|
||||||
@@ -1232,7 +1186,139 @@ void Renderer::Impl::upload_mesh(const Mesh& mesh) {
|
|||||||
create_texture(resource, mesh.texture);
|
create_texture(resource, mesh.texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
|
void Renderer::Impl::recreate_swapchain() {
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
glfwGetFramebufferSize(window, &width, &height);
|
||||||
|
if (width == 0 || height == 0) return;
|
||||||
|
check(vkDeviceWaitIdle(device), "vkDeviceWaitIdle");
|
||||||
|
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities{};
|
||||||
|
check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities),
|
||||||
|
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||||
|
uint32_t format_count = 0;
|
||||||
|
check(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, nullptr),
|
||||||
|
"vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||||
|
std::vector<VkSurfaceFormatKHR> formats(format_count);
|
||||||
|
check(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, formats.data()),
|
||||||
|
"vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||||
|
VkSurfaceFormatKHR surface_format = formats.front();
|
||||||
|
for (const VkSurfaceFormatKHR& format : formats) {
|
||||||
|
if (format.format == VK_FORMAT_B8G8R8A8_SRGB &&
|
||||||
|
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
surface_format = format;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (surface_format.format != swapchain_format) {
|
||||||
|
throw std::runtime_error("swapchain format changed; renderer recreation is required");
|
||||||
|
}
|
||||||
|
uint32_t present_mode_count = 0;
|
||||||
|
check(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count,
|
||||||
|
nullptr),
|
||||||
|
"vkGetPhysicalDeviceSurfacePresentModesKHR");
|
||||||
|
std::vector<VkPresentModeKHR> present_modes(present_mode_count);
|
||||||
|
check(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count,
|
||||||
|
present_modes.data()),
|
||||||
|
"vkGetPhysicalDeviceSurfacePresentModesKHR");
|
||||||
|
VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
for (VkPresentModeKHR mode : present_modes) {
|
||||||
|
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||||
|
present_mode = mode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VkExtent2D extent{};
|
||||||
|
if (capabilities.currentExtent.width != UINT32_MAX) {
|
||||||
|
extent = capabilities.currentExtent;
|
||||||
|
} else {
|
||||||
|
extent.width = std::clamp(static_cast<uint32_t>(width), capabilities.minImageExtent.width,
|
||||||
|
capabilities.maxImageExtent.width);
|
||||||
|
extent.height = std::clamp(static_cast<uint32_t>(height), capabilities.minImageExtent.height,
|
||||||
|
capabilities.maxImageExtent.height);
|
||||||
|
}
|
||||||
|
uint32_t image_count = capabilities.minImageCount + 1;
|
||||||
|
if (capabilities.maxImageCount > 0 && image_count > capabilities.maxImageCount) {
|
||||||
|
image_count = capabilities.maxImageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (VkFramebuffer framebuffer : framebuffers) {
|
||||||
|
vkDestroyFramebuffer(device, framebuffer, nullptr);
|
||||||
|
}
|
||||||
|
framebuffers.clear();
|
||||||
|
for (VkImageView view : swapchain_image_views) {
|
||||||
|
vkDestroyImageView(device, view, nullptr);
|
||||||
|
}
|
||||||
|
swapchain_image_views.clear();
|
||||||
|
const VkSwapchainKHR old_swapchain = swapchain;
|
||||||
|
if (old_swapchain != VK_NULL_HANDLE) {
|
||||||
|
vkDestroySwapchainKHR(device, old_swapchain, nullptr);
|
||||||
|
swapchain = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSwapchainCreateInfoKHR create_info{};
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
create_info.surface = surface;
|
||||||
|
create_info.minImageCount = image_count;
|
||||||
|
create_info.imageFormat = surface_format.format;
|
||||||
|
create_info.imageColorSpace = surface_format.colorSpace;
|
||||||
|
create_info.imageExtent = extent;
|
||||||
|
create_info.imageArrayLayers = 1;
|
||||||
|
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
|
std::array<uint32_t, 2> families = {graphics_family, present_family};
|
||||||
|
create_info.imageSharingMode = graphics_family == present_family
|
||||||
|
? VK_SHARING_MODE_EXCLUSIVE
|
||||||
|
: VK_SHARING_MODE_CONCURRENT;
|
||||||
|
create_info.queueFamilyIndexCount = graphics_family == present_family ? 0 : 2;
|
||||||
|
create_info.pQueueFamilyIndices = families.data();
|
||||||
|
create_info.preTransform = capabilities.currentTransform;
|
||||||
|
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
create_info.presentMode = present_mode;
|
||||||
|
create_info.clipped = VK_TRUE;
|
||||||
|
create_info.oldSwapchain = VK_NULL_HANDLE;
|
||||||
|
check(vkCreateSwapchainKHR(device, &create_info, nullptr, &swapchain), "vkCreateSwapchainKHR");
|
||||||
|
|
||||||
|
swapchain_extent = extent;
|
||||||
|
uint32_t actual_image_count = 0;
|
||||||
|
check(vkGetSwapchainImagesKHR(device, swapchain, &actual_image_count, nullptr),
|
||||||
|
"vkGetSwapchainImagesKHR");
|
||||||
|
if (actual_image_count > frames.size()) {
|
||||||
|
throw std::runtime_error("resized swapchain needs more frame resources");
|
||||||
|
}
|
||||||
|
swapchain_images.resize(actual_image_count);
|
||||||
|
check(vkGetSwapchainImagesKHR(device, swapchain, &actual_image_count, swapchain_images.data()),
|
||||||
|
"vkGetSwapchainImagesKHR");
|
||||||
|
swapchain_image_views.resize(actual_image_count);
|
||||||
|
for (uint32_t i = 0; i < actual_image_count; ++i) {
|
||||||
|
VkImageViewCreateInfo view_info{};
|
||||||
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
view_info.image = swapchain_images[i];
|
||||||
|
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
view_info.format = swapchain_format;
|
||||||
|
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
view_info.subresourceRange.levelCount = 1;
|
||||||
|
view_info.subresourceRange.layerCount = 1;
|
||||||
|
check(vkCreateImageView(device, &view_info, nullptr, &swapchain_image_views[i]),
|
||||||
|
"vkCreateImageView");
|
||||||
|
}
|
||||||
|
framebuffers.resize(actual_image_count);
|
||||||
|
for (uint32_t i = 0; i < actual_image_count; ++i) {
|
||||||
|
VkFramebufferCreateInfo framebuffer_info{};
|
||||||
|
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||||
|
framebuffer_info.renderPass = render_pass;
|
||||||
|
framebuffer_info.attachmentCount = 1;
|
||||||
|
framebuffer_info.pAttachments = &swapchain_image_views[i];
|
||||||
|
framebuffer_info.width = swapchain_extent.width;
|
||||||
|
framebuffer_info.height = swapchain_extent.height;
|
||||||
|
framebuffer_info.layers = 1;
|
||||||
|
check(vkCreateFramebuffer(device, &framebuffer_info, nullptr, &framebuffers[i]),
|
||||||
|
"vkCreateFramebuffer");
|
||||||
|
}
|
||||||
|
images_in_flight.assign(actual_image_count, VK_NULL_HANDLE);
|
||||||
|
framebuffer_resized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count, const Camera* camera) {
|
||||||
if (!initialized || actor_count == 0) {
|
if (!initialized || actor_count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1245,6 +1331,7 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
|
|||||||
frame.image_available,
|
frame.image_available,
|
||||||
VK_NULL_HANDLE, &image_index);
|
VK_NULL_HANDLE, &image_index);
|
||||||
if (acquire_result == VK_ERROR_OUT_OF_DATE_KHR) {
|
if (acquire_result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||||||
|
recreate_swapchain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (acquire_result != VK_SUCCESS && acquire_result != VK_SUBOPTIMAL_KHR) {
|
if (acquire_result != VK_SUCCESS && acquire_result != VK_SUBOPTIMAL_KHR) {
|
||||||
@@ -1286,6 +1373,15 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
|
|||||||
|
|
||||||
vkCmdBeginRenderPass(frame.command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdBeginRenderPass(frame.command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
vkCmdBindPipeline(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
vkCmdBindPipeline(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
VkViewport viewport{};
|
||||||
|
viewport.width = static_cast<float>(swapchain_extent.width);
|
||||||
|
viewport.height = static_cast<float>(swapchain_extent.height);
|
||||||
|
viewport.minDepth = 0.0F;
|
||||||
|
viewport.maxDepth = 1.0F;
|
||||||
|
VkRect2D scissor{};
|
||||||
|
scissor.extent = swapchain_extent;
|
||||||
|
vkCmdSetViewport(frame.command_buffer, 0, 1, &viewport);
|
||||||
|
vkCmdSetScissor(frame.command_buffer, 0, 1, &scissor);
|
||||||
for (size_t i = 0; i < actor_count; ++i) {
|
for (size_t i = 0; i < actor_count; ++i) {
|
||||||
const Actor& actor = *actors[i];
|
const Actor& actor = *actors[i];
|
||||||
if (actor.mesh() == nullptr) continue;
|
if (actor.mesh() == nullptr) continue;
|
||||||
@@ -1301,9 +1397,26 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
|
|||||||
vkCmdBindDescriptorSets(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
vkCmdBindDescriptorSets(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
|
||||||
0, 1, &resource.descriptor_set, 0, nullptr);
|
0, 1, &resource.descriptor_set, 0, nullptr);
|
||||||
|
|
||||||
const auto position = actor.position();
|
auto position = actor.global_position();
|
||||||
const auto facing = actor.facing();
|
auto facing = actor.global_facing();
|
||||||
const float scale = 0.35F;
|
float camera_zoom = 1.0F;
|
||||||
|
if (camera != nullptr) {
|
||||||
|
const auto camera_position = camera->global_position();
|
||||||
|
const auto camera_facing = camera->global_facing();
|
||||||
|
const float camera_length = std::sqrt(camera_facing[0] * camera_facing[0] +
|
||||||
|
camera_facing[1] * camera_facing[1]);
|
||||||
|
const float camera_x = camera_length > 0.0001F ? camera_facing[0] / camera_length : 1.0F;
|
||||||
|
const float camera_y = camera_length > 0.0001F ? camera_facing[1] / camera_length : 0.0F;
|
||||||
|
const float dx = position[0] - camera_position[0];
|
||||||
|
const float dy = position[1] - camera_position[1];
|
||||||
|
position = {camera_x * dx + camera_y * dy, -camera_y * dx + camera_x * dy};
|
||||||
|
facing = {camera_x * facing[0] + camera_y * facing[1],
|
||||||
|
-camera_y * facing[0] + camera_x * facing[1]};
|
||||||
|
camera_zoom = camera->zoom();
|
||||||
|
}
|
||||||
|
const float scale = 0.35F * camera_zoom;
|
||||||
|
const float horizontal_projection =
|
||||||
|
static_cast<float>(swapchain_extent.height) / static_cast<float>(swapchain_extent.width);
|
||||||
float facing_x = facing[0];
|
float facing_x = facing[0];
|
||||||
float facing_y = facing[1];
|
float facing_y = facing[1];
|
||||||
const float facing_length = std::sqrt(facing_x * facing_x + facing_y * facing_y);
|
const float facing_length = std::sqrt(facing_x * facing_x + facing_y * facing_y);
|
||||||
@@ -1316,10 +1429,10 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::array<float, 20> constants = {{
|
std::array<float, 20> constants = {{
|
||||||
scale * facing_x, scale * facing_y, 0.0F, 0.0F,
|
scale * facing_x * horizontal_projection, scale * facing_y, 0.0F, 0.0F,
|
||||||
-scale * facing_y, scale * facing_x, 0.0F, 0.0F,
|
-scale * facing_y * horizontal_projection, scale * facing_x, 0.0F, 0.0F,
|
||||||
0.0F, 0.0F, 1.0F, 0.0F,
|
0.0F, 0.0F, 1.0F, 0.0F,
|
||||||
position[0], position[1], 0.0F, 1.0F,
|
position[0] * horizontal_projection, position[1], 0.0F, 1.0F,
|
||||||
0.0F, 0.0F, 0.0F, 0.0F,
|
0.0F, 0.0F, 0.0F, 0.0F,
|
||||||
}};
|
}};
|
||||||
const std::array<float, 4> color = actor.mesh()->color();
|
const std::array<float, 4> color = actor.mesh()->color();
|
||||||
@@ -1354,8 +1467,10 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
|
|||||||
present_info.pSwapchains = &swapchain;
|
present_info.pSwapchains = &swapchain;
|
||||||
present_info.pImageIndices = &image_index;
|
present_info.pImageIndices = &image_index;
|
||||||
VkResult present_result = vkQueuePresentKHR(present_queue, &present_info);
|
VkResult present_result = vkQueuePresentKHR(present_queue, &present_info);
|
||||||
if (present_result != VK_SUCCESS && present_result != VK_SUBOPTIMAL_KHR &&
|
if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR ||
|
||||||
present_result != VK_ERROR_OUT_OF_DATE_KHR) {
|
framebuffer_resized) {
|
||||||
|
recreate_swapchain();
|
||||||
|
} else if (present_result != VK_SUCCESS) {
|
||||||
check(present_result, "vkQueuePresentKHR");
|
check(present_result, "vkQueuePresentKHR");
|
||||||
}
|
}
|
||||||
(void)present_result;
|
(void)present_result;
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#include <render.hpp>
|
||||||
|
|
||||||
|
namespace boundard::render {
|
||||||
|
|
||||||
|
RenderLoop::~RenderLoop() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderLoop::start(int width, int height, const char* title) {
|
||||||
|
if (running_) return;
|
||||||
|
|
||||||
|
renderer_.init(width, height, title);
|
||||||
|
renderer_.set_input_callback([this](const InputEvent& event) {
|
||||||
|
input_map_.handle_event(event);
|
||||||
|
});
|
||||||
|
running_ = true;
|
||||||
|
thread_ = std::thread(&RenderLoop::run, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderLoop::stop() {
|
||||||
|
if (!running_) return;
|
||||||
|
running_ = false;
|
||||||
|
if (thread_.joinable()) thread_.join();
|
||||||
|
renderer_.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderLoop::set_actors(std::vector<Actor*> actors) {
|
||||||
|
std::lock_guard<std::mutex> lock(actors_mutex_);
|
||||||
|
actors_ = std::move(actors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderLoop::set_camera(Camera* camera) {
|
||||||
|
std::lock_guard<std::mutex> lock(actors_mutex_);
|
||||||
|
camera_ = camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderLoop::poll_events() const {
|
||||||
|
renderer_.poll_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenderLoop::should_close() const {
|
||||||
|
return renderer_.should_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputMap& RenderLoop::input() {
|
||||||
|
return input_map_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InputMap& RenderLoop::input() const {
|
||||||
|
return input_map_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderLoop::run() {
|
||||||
|
while (running_) {
|
||||||
|
std::vector<Actor*> actors;
|
||||||
|
Camera* camera = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(actors_mutex_);
|
||||||
|
actors = actors_;
|
||||||
|
camera = camera_;
|
||||||
|
}
|
||||||
|
if (actors.empty()) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
renderer_.draw(actors.data(), actors.size(), camera);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace boundard::render
|
||||||
+50
-2
@@ -1,10 +1,38 @@
|
|||||||
#include <scene.hpp>
|
#include <scene.hpp>
|
||||||
|
|
||||||
#include <actor.hpp>
|
#include <actor.hpp>
|
||||||
|
#include <camera.hpp>
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace boundard {
|
namespace boundard {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void append_actor_tree(Actor* actor, std::vector<Actor*>& output) {
|
||||||
|
if (actor == nullptr) return;
|
||||||
|
output.push_back(actor);
|
||||||
|
for (const std::shared_ptr<Actor>& child : actor->children()) {
|
||||||
|
append_actor_tree(child.get(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void find_cameras(Actor* actor, const std::string& name, Camera*& first_camera,
|
||||||
|
Camera*& selected_camera) {
|
||||||
|
if (actor == nullptr || selected_camera != nullptr) return;
|
||||||
|
Camera* camera = dynamic_cast<Camera*>(actor);
|
||||||
|
if (camera != nullptr) {
|
||||||
|
if (first_camera == nullptr) first_camera = camera;
|
||||||
|
if (!name.empty() && camera->name() == name) {
|
||||||
|
selected_camera = camera;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const std::shared_ptr<Actor>& child : actor->children()) {
|
||||||
|
find_cameras(child.get(), name, first_camera, selected_camera);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Scene::Scene(std::string name) : name_(std::move(name)) {}
|
Scene::Scene(std::string name) : name_(std::move(name)) {}
|
||||||
|
|
||||||
@@ -36,24 +64,44 @@ void Scene::set_actors(std::vector<std::shared_ptr<Actor>> actors) {
|
|||||||
void Scene::clear() {
|
void Scene::clear() {
|
||||||
actors_.clear();
|
actors_.clear();
|
||||||
actor_storage_.clear();
|
actor_storage_.clear();
|
||||||
|
default_camera_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::set_default_camera(std::string name) {
|
||||||
|
default_camera_ = std::move(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera* Scene::primary_camera() const {
|
||||||
|
Camera* first_camera = nullptr;
|
||||||
|
Camera* selected_camera = nullptr;
|
||||||
|
for (const std::shared_ptr<Actor>& actor : actor_storage_) {
|
||||||
|
find_cameras(actor.get(), default_camera_, first_camera, selected_camera);
|
||||||
|
if (selected_camera != nullptr) return selected_camera;
|
||||||
|
}
|
||||||
|
return first_camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<Actor*>& Scene::actors() const {
|
const std::vector<Actor*>& Scene::actors() const {
|
||||||
|
actors_.clear();
|
||||||
|
for (const std::shared_ptr<Actor>& actor : actor_storage_) {
|
||||||
|
append_actor_tree(actor.get(), actors_);
|
||||||
|
}
|
||||||
return actors_;
|
return actors_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Actor*>& Scene::actors() {
|
std::vector<Actor*>& Scene::actors() {
|
||||||
|
static_cast<const Scene&>(*this).actors();
|
||||||
return actors_;
|
return actors_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::initialize() {
|
void Scene::initialize() {
|
||||||
for (Actor* actor : actors_) {
|
for (const std::shared_ptr<Actor>& actor : actor_storage_) {
|
||||||
actor->initialize();
|
actor->initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::process(float dt, const render::InputMap& input) {
|
void Scene::process(float dt, const render::InputMap& input) {
|
||||||
for (Actor* actor : actors_) {
|
for (const std::shared_ptr<Actor>& actor : actor_storage_) {
|
||||||
actor->process(dt, input);
|
actor->process(dt, input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user