Get player controller wall collisions working

This commit is contained in:
bailehuni 2024-03-18 01:04:33 +00:00
parent a4d58a1490
commit 1b0af6cd8b
19 changed files with 468 additions and 322 deletions

View File

71
cloc
View File

@ -1,71 +0,0 @@
include/application.h
include/components/collider.h
include/components/custom.h
include/components/mesh_renderable.h
include/components/transform.h
include/components/ui_renderable.h
include/ecs.h
include/engine_api.h
include/event_system.h
include/gfx.h
include/gfx_device.h
include/input_manager.h
include/inputs/keyboard.h
include/inputs/mouse.h
include/log.h
include/logger.h
include/renderer.h
include/resource_manager.h
include/resources/font.h
include/resources/material.h
include/resources/mesh.h
include/resources/shader.h
include/resources/texture.h
include/scene.h
include/scene_manager.h
include/systems/collisions.h
include/systems/custom_behaviour.h
include/systems/mesh_render_system.h
include/systems/transform.h
include/systems/ui_render_system.h
include/util.h
include/util/files.h
include/util/gltf_loader.h
include/util/model_loader.h
include/window.h
src/application.cpp
src/ecs.cpp
src/gfx_device_vulkan.cpp
src/input_manager.cpp
src/renderer.cpp
src/resources/font.cpp
src/resources/material.cpp
src/resources/mesh.cpp
src/resources/shader.cpp
src/resources/texture.cpp
src/scene.cpp
src/scene_manager.cpp
src/systems/collisions.cpp
src/systems/custom_behaviour.cpp
src/systems/mesh_render_system.cpp
src/systems/transform.cpp
src/systems/ui_render_system.cpp
src/util/files.cpp
src/util/gltf_loader.cpp
src/util/model_loader.cpp
src/vulkan/device.cpp
src/vulkan/device.h
src/vulkan/gpu_allocator.cpp
src/vulkan/gpu_allocator.h
src/vulkan/instance.cpp
src/vulkan/instance.h
src/vulkan/swapchain.cpp
src/vulkan/swapchain.h
src/window.cpp
test/src/camera_controller.cpp
test/src/camera_controller.hpp
test/src/game.cpp
test/src/game.hpp
test/src/main.cpp
test/src/meshgen.cpp
test/src/meshgen.hpp

View File

@ -66,6 +66,8 @@ class Application {
std::string GetResourcePath(const std::string relative_path) const { return (resources_path_ / relative_path).string(); }
std::vector<Line> debug_lines{};
private:
std::unique_ptr<Window> window_;
std::unique_ptr<InputManager> input_manager_;

View File

@ -27,6 +27,7 @@ struct UniformDescriptor {
struct Line {
glm::vec3 pos1;
glm::vec3 pos2;
glm::vec3 color;
};
class Renderer : private ApplicationComponent {

View File

@ -18,11 +18,14 @@ struct Ray {
struct Raycast {
glm::vec3 location;
Entity hit_entity;
glm::vec3 normal;
Entity hit_entity; // broken
float distance;
bool hit;
};
enum class AABBSide { Left, Right, Bottom, Top, Front, Back };
class CollisionSystem : public System {
public:
CollisionSystem(Scene* scene);
@ -59,7 +62,14 @@ class CollisionSystem : public System {
size_t colliders_size_last_update_ = 0;
size_t colliders_size_now_ = 0;
bool RaycastTreeNode(const Ray& ray, const BiTreeNode& node, glm::vec3& location, float& t, Entity& object_index);
struct RaycastTreeNodeResult {
glm::vec3 location;
Entity object_index;
AABBSide side;
float t;
bool hit; // if this is false, all other values are undefined
};
RaycastTreeNodeResult RaycastTreeNode(const Ray& ray, const BiTreeNode& node);
static int BuildNode(std::vector<PrimitiveInfo>& prims, std::vector<BiTreeNode>& tree_nodes);
};

View File

@ -1,7 +1,9 @@
#version 450
layout(location = 0) in vec3 inColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(1.0);
outColor = vec4(inColor, 1.0);
}

View File

@ -2,9 +2,13 @@
layout( push_constant ) uniform Constants {
vec4 positions[2];
vec3 color;
} constants;
layout(location = 0) out vec3 color;
void main() {
color = constants.color;
gl_Position = constants.positions[gl_VertexIndex];
gl_Position.y *= -1.0;
}

View File

@ -29,8 +29,8 @@ float GGXDist(float alpha_2, float N_dot_H) {
void main() {
const vec3 metallic_roughness = vec3(texture(materialSetMetallicRoughnessSampler, fragUV));
const float metallic = metallic_roughness.g;
const float roughness = metallic_roughness.b;
const float metallic = metallic_roughness.b;
const float roughness = metallic_roughness.g; // roughness of zero is completely black?
const float roughness_2 = roughness * roughness;
const vec3 light_colour = vec3(1.0, 1.0, 1.0) * 2.4;

View File

@ -34,7 +34,7 @@ void main() {
fragUV = inUV;
fragPosTangentSpace = worldToTangentSpace * vec3(worldPosition);
fragViewPosTangentSpace = worldToTangentSpace * vec3(inverse(frameSetUniformBuffer.view) * vec4(0.0, 0.0, 0.0, 1.0));
fragLightPosTangentSpace = worldToTangentSpace * vec3(10000.0, 0000.0, 59000.0);
fragLightPosTangentSpace = worldToTangentSpace * vec3(10000.0, 0000.0, 20000.0);
gl_Position.y *= -1.0;
}

View File

@ -168,7 +168,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph
GetResourceManager<Texture>()->AddPersistent("builtin.normal", std::move(normalTexture));
}
{
const uint8_t pixel[4] = {255, 0, 127, 255};
const uint8_t pixel[4] = {255, 127, 0, 255}; // AO, roughness, metallic
gfx::SamplerInfo samplerInfo{};
samplerInfo.minify = gfx::Filter::kNearest;
samplerInfo.magnify = gfx::Filter::kNearest;
@ -208,7 +208,8 @@ void Application::GameLoop()
struct DebugMenuState {
bool menu_active = false;
bool show_info_window = true;
bool show_aabbs = false;
bool show_info_window = false;
} debug_menu_state;
// single-threaded game loop
@ -245,6 +246,7 @@ void Application::GameLoop()
if (ImGui::Begin("debugMenu", 0)) {
ImGui::Text("Test!");
ImGui::Text("FPS: %.3f", std::roundf(avg_fps));
ImGui::Checkbox("Show AABBs?", &debug_menu_state.show_aabbs);
}
ImGui::End();
}
@ -285,8 +287,73 @@ void Application::GameLoop()
const RenderList* static_list = nullptr;
const RenderList* dynamic_list = nullptr;
glm::mat4 camera_transform{1.0f};
std::vector<Line> debug_lines{};
if (scene) {
if (debug_menu_state.show_aabbs) {
if (CollisionSystem* colsys = scene->GetSystem<CollisionSystem>()) {
for (const auto& node : colsys->bvh_) {
if (node.type1 == CollisionSystem::BiTreeNode::Type::Entity) {
const glm::vec3 col =
(node.type1 == CollisionSystem::BiTreeNode::Type::BoundingVolume) ? glm::vec3{ 1.0f, 0.0f, 0.0f } : glm::vec3{ 0.0f, 1.0f, 0.0f };
Line line1{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, col };
debug_lines.push_back(line1);
Line line2{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, col };
debug_lines.push_back(line2);
Line line3{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, col };
debug_lines.push_back(line3);
Line line4{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, col };
debug_lines.push_back(line4);
Line line5{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line5);
Line line6{ glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line6);
Line line7{ glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line7);
Line line8{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.min.z}, glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line8);
Line line9{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line9);
Line line10{ glm::vec3{node.box1.min.x, node.box1.min.y, node.box1.max.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line10);
Line line11{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, glm::vec3{node.box1.max.x, node.box1.min.y, node.box1.max.z}, col };
debug_lines.push_back(line11);
Line line12{ glm::vec3{node.box1.max.x, node.box1.max.y, node.box1.max.z}, glm::vec3{node.box1.min.x, node.box1.max.y, node.box1.max.z}, col };
debug_lines.push_back(line12);
}
if (node.type2 == CollisionSystem::BiTreeNode::Type::Entity) {
const glm::vec3 col =
(node.type2 == CollisionSystem::BiTreeNode::Type::BoundingVolume) ? glm::vec3{ 1.0f, 0.0f, 0.0f } : glm::vec3{ 0.0f, 1.0f, 0.0f };
Line line1{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, col };
debug_lines.push_back(line1);
Line line2{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, col };
debug_lines.push_back(line2);
Line line3{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, col };
debug_lines.push_back(line3);
Line line4{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, col };
debug_lines.push_back(line4);
Line line5{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line5);
Line line6{ glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line6);
Line line7{ glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line7);
Line line8{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.min.z}, glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line8);
Line line9{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line9);
Line line10{ glm::vec3{node.box2.min.x, node.box2.min.y, node.box2.max.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line10);
Line line11{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, glm::vec3{node.box2.max.x, node.box2.min.y, node.box2.max.z}, col };
debug_lines.push_back(line11);
Line line12{ glm::vec3{node.box2.max.x, node.box2.max.y, node.box2.max.z}, glm::vec3{node.box2.min.x, node.box2.max.y, node.box2.max.z}, col };
debug_lines.push_back(line12);
}
}
}
}
camera_transform = scene->GetComponent<TransformComponent>(scene->GetEntity("camera"))->world_matrix;
auto mesh_render_system = scene->GetSystem<MeshRenderSystem>();
static_list = mesh_render_system->GetStaticRenderList();
@ -294,6 +361,7 @@ void Application::GameLoop()
}
renderer_->PreRender(window()->GetWindowResized(), camera_transform);
renderer_->Render(static_list, dynamic_list, debug_lines);
debug_lines.clear(); // gets remade every frame :0
/* poll events */
window_->GetInputAndEvents();

View File

@ -121,24 +121,32 @@ void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_l
}
}
struct DebugPush {
glm::vec4 pos1;
glm::vec4 pos2;
glm::vec3 color;
};
// draw debug shit here
device_->CmdBindPipeline(draw_buffer, debug_rendering_things_.pipeline);
glm::vec4 debug_positions[2] = {};
DebugPush push{};
for (const Line& l : debug_lines) {
debug_positions[0] = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos1, 1.0f);
debug_positions[1] = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos2, 1.0f);
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(glm::vec4) * 2, debug_positions);
push.pos1 = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos1, 1.0f);
push.pos2 = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos2, 1.0f);
push.color = l.color;
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push);
device_->CmdDraw(draw_buffer, 2, 1, 0, 0);
}
// also make a lil crosshair
debug_positions[0] = glm::vec4(-0.05f, 0.0f, 0.0f, 1.0f);
debug_positions[1] = glm::vec4(0.05f, 0.0f, 0.0f, 1.0f);
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(glm::vec4) * 2, debug_positions);
push.color = glm::vec3{ 1.0f, 1.0f, 1.0f };
push.pos1 = glm::vec4(-0.05f, 0.0f, 0.0f, 1.0f);
push.pos2 = glm::vec4(0.05f, 0.0f, 0.0f, 1.0f);
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push);
device_->CmdDraw(draw_buffer, 2, 1, 0, 0);
debug_positions[0] = glm::vec4(0.0f, -0.05f, 0.0f, 1.0f);
debug_positions[1] = glm::vec4(0.0f, 0.05f, 0.0f, 1.0f);
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(glm::vec4) * 2, debug_positions);
push.pos1 = glm::vec4(0.0f, -0.05f, 0.0f, 1.0f);
push.pos2 = glm::vec4(0.0f, 0.05f, 0.0f, 1.0f);
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push);
device_->CmdDraw(draw_buffer, 2, 1, 0, 0);
device_->CmdRenderImguiDrawData(draw_buffer, ImGui::GetDrawData());

View File

@ -29,7 +29,8 @@ static float GetBoxArea(const AABB& box)
}
// returns true on hit and tmin
static std::pair<bool, float> RayBoxIntersection(const Ray& ray, const AABB& box, float t)
// also modifies 'side' reference
static std::pair<bool, float> RayBoxIntersection(const Ray& ray, const AABB& box, float t, AABBSide& side)
{
// Thank you https://tavianator.com/cgit/dimension.git/tree/libdimension/bvh/bvh.c
glm::vec3 n_inv{1.0f / ray.direction.x, 1.0f / ray.direction.y, 1.0f / ray.direction.z};
@ -52,6 +53,20 @@ static std::pair<bool, float> RayBoxIntersection(const Ray& ray, const AABB& box
tmin = fmaxf(tmin, fminf(tz1, tz2));
tmax = fminf(tmax, fmaxf(tz1, tz2));
side = AABBSide::Left;
if (tmin == tx2)
side = AABBSide::Right;
else if (tmin == tx1)
side = AABBSide::Left;
else if (tmin == ty2)
side = AABBSide::Back;
else if (tmin == ty1)
side = AABBSide::Front;
else if (tmin == tz2)
side = AABBSide::Top;
else if (tmin == tz1)
side = AABBSide::Bottom;
return std::make_pair((tmax >= fmaxf(0.0, tmin) && tmin < t), tmin);
}
@ -117,13 +132,40 @@ void CollisionSystem::OnUpdate(float ts)
Raycast CollisionSystem::GetRaycast(Ray ray)
{
ray.direction = glm::normalize(ray.direction);
Raycast res{};
res.hit = false;
ray.direction = glm::normalize(ray.direction);
if (!bvh_.empty()) {
res.hit = RaycastTreeNode(ray, bvh_.back(), res.location, res.distance, res.hit_entity);
const RaycastTreeNodeResult tree_node_cast_res = RaycastTreeNode(ray, bvh_.back());
if (tree_node_cast_res.hit) {
res.hit = true;
res.distance = tree_node_cast_res.t;
res.location = tree_node_cast_res.location;
res.hit_entity = tree_node_cast_res.object_index;
// find normal
switch (tree_node_cast_res.side) {
case AABBSide::Left:
res.normal = glm::vec3{-1.0, 0.0f, 0.0f};
break;
case AABBSide::Right:
res.normal = glm::vec3{1.0, 0.0f, 0.0f};
break;
case AABBSide::Bottom:
res.normal = glm::vec3{0.0, 0.0f, -1.0f};
break;
case AABBSide::Top:
res.normal = glm::vec3{0.0, 0.0f, 1.0f};
break;
case AABBSide::Front:
res.normal = glm::vec3{0.0, -1.0f, 0.0f};
break;
case AABBSide::Back:
res.normal = glm::vec3{0.0, 1.0f, 0.0f};
break;
}
}
}
return res;
@ -139,38 +181,17 @@ int CollisionSystem::BuildNode(std::vector<PrimitiveInfo>& prims, std::vector<Bi
if (prims.size() == 0) abort();
std::array<std::function<bool(const PrimitiveInfo&, const PrimitiveInfo&)>, 3> centroid_tests{
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.centroid.x < p2.centroid.x);
},
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.centroid.y < p2.centroid.y);
},
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.centroid.z < p2.centroid.z);
}
};
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.centroid.x < p2.centroid.x); },
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.centroid.y < p2.centroid.y); },
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.centroid.z < p2.centroid.z); }};
std::array<std::function<bool(const PrimitiveInfo&, const PrimitiveInfo&)>, 3> box_min_tests{
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.box.min.x < p2.box.min.x);
},
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.box.min.y < p2.box.min.y);
},
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.box.min.z < p2.box.min.z);
}
};
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.min.x < p2.box.min.x); },
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.min.y < p2.box.min.y); },
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.min.z < p2.box.min.z); }};
std::array<std::function<bool(const PrimitiveInfo&, const PrimitiveInfo&)>, 3> box_max_tests{
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.box.max.x < p2.box.max.x);
},
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.box.max.y < p2.box.max.y);
},
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool {
return (p1.box.max.z < p2.box.max.z);
}
};
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.max.x < p2.box.max.x); },
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.max.y < p2.box.max.y); },
[](const PrimitiveInfo& p1, const PrimitiveInfo& p2) -> bool { return (p1.box.max.z < p2.box.max.z); }};
BiTreeNode node{};
@ -201,20 +222,32 @@ int CollisionSystem::BuildNode(std::vector<PrimitiveInfo>& prims, std::vector<Bi
// split the boxes
for (int i = 0; i < prims.size() - 1; i++) {
float box1_main_min = reinterpret_cast<const float*>(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[axis])->box.min))[axis];
float box1_main_max = reinterpret_cast<const float*>(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[axis])->box.max))[axis];
float box2_main_min = reinterpret_cast<const float*>(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[axis])->box.min))[axis];
float box2_main_max = reinterpret_cast<const float*>(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[axis])->box.max))[axis];
float box1_main_min =
reinterpret_cast<const float*>(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[axis])->box.min))[axis];
float box1_main_max =
reinterpret_cast<const float*>(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[axis])->box.max))[axis];
float box2_main_min =
reinterpret_cast<const float*>(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[axis])->box.min))[axis];
float box2_main_max =
reinterpret_cast<const float*>(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[axis])->box.max))[axis];
float box1_min1 = reinterpret_cast<const float*>(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis1])->box.min))[other_axis1];
float box1_max1 = reinterpret_cast<const float*>(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis1])->box.max))[other_axis1];
float box1_min2 = reinterpret_cast<const float*>(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis2])->box.min))[other_axis2];
float box1_max2 = reinterpret_cast<const float*>(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis2])->box.max))[other_axis2];
float box1_min1 =
reinterpret_cast<const float*>(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis1])->box.min))[other_axis1];
float box1_max1 =
reinterpret_cast<const float*>(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis1])->box.max))[other_axis1];
float box1_min2 =
reinterpret_cast<const float*>(&(std::min_element(prims.begin(), prims.begin() + i + 1, box_min_tests[other_axis2])->box.min))[other_axis2];
float box1_max2 =
reinterpret_cast<const float*>(&(std::max_element(prims.begin(), prims.begin() + i + 1, box_max_tests[other_axis2])->box.max))[other_axis2];
float box2_min1 = reinterpret_cast<const float*>(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis1])->box.min))[other_axis1];
float box2_max1 = reinterpret_cast<const float*>(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis1])->box.max))[other_axis1];
float box2_min2 = reinterpret_cast<const float*>(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis2])->box.min))[other_axis2];
float box2_max2 = reinterpret_cast<const float*>(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis2])->box.max))[other_axis2];
float box2_min1 =
reinterpret_cast<const float*>(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis1])->box.min))[other_axis1];
float box2_max1 =
reinterpret_cast<const float*>(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis1])->box.max))[other_axis1];
float box2_min2 =
reinterpret_cast<const float*>(&(std::min_element(prims.begin() + i + 1, prims.end(), box_min_tests[other_axis2])->box.min))[other_axis2];
float box2_max2 =
reinterpret_cast<const float*>(&(std::max_element(prims.begin() + i + 1, prims.end(), box_max_tests[other_axis2])->box.max))[other_axis2];
AABB box1{}, box2{};
switch (axis) {
@ -290,7 +323,6 @@ int CollisionSystem::BuildNode(std::vector<PrimitiveInfo>& prims, std::vector<Bi
node.index2 = index2;
node.type1 = BiTreeNode::Type::BoundingVolume;
node.type2 = BiTreeNode::Type::BoundingVolume;
}
else {
if (prims.size() == 2) {
@ -317,142 +349,161 @@ int CollisionSystem::BuildNode(std::vector<PrimitiveInfo>& prims, std::vector<Bi
}
// returns true on ray hit
bool CollisionSystem::RaycastTreeNode(const Ray& ray, const BiTreeNode& node, glm::vec3& location, float& t, Entity& object_index)
CollisionSystem::RaycastTreeNodeResult CollisionSystem::RaycastTreeNode(const Ray& ray, const BiTreeNode& node)
{
using Type = BiTreeNode::Type;
constexpr float T = 1000.0f;
RaycastTreeNodeResult res{};
bool is_hit1 = false;
bool is_hit2 = false;
float t1 = std::numeric_limits<float>::infinity();
float t2 = std::numeric_limits<float>::infinity();
AABBSide side1;
AABBSide side2;
if (node.type1 != Type::Empty) {
auto [is_hit, t] = RayBoxIntersection(ray, node.box1, T);
auto [is_hit, t] = RayBoxIntersection(ray, node.box1, T, side1);
is_hit1 = is_hit;
t1 = t;
}
if (node.type2 != Type::Empty) {
auto [is_hit, t] = RayBoxIntersection(ray, node.box2, T);
auto [is_hit, t] = RayBoxIntersection(ray, node.box2, T, side2);
is_hit2 = is_hit;
t2 = t;
}
// possible outcomes:
// neither hit
if (!is_hit1 && !is_hit2) return false;
// when both hit
if (is_hit1 && is_hit2) {
if (!is_hit1 && !is_hit2) {
res.hit = false;
return res;
}
else if (is_hit1 && is_hit2) {
// if 1 is a BV and 2 a gameobject, see if gameobject is in front
if (node.type1 == Type::BoundingVolume && node.type2 == Type::Entity) {
if (t2 < t1) {
location = (ray.direction * t2) + ray.origin;
t = t2;
object_index = node.index2;
return true;
res.location = (ray.direction * t2) + ray.origin;
res.t = t2;
res.object_index = node.index2;
res.side = side2;
res.hit = true;
return res;
}
else
return RaycastTreeNode(ray, bvh_.at(node.index1), location, t, object_index);
return RaycastTreeNode(ray, bvh_.at(node.index1));
}
// if 1 is a gameobject and 2 a BV, see if gameobject is in front
if (node.type1 == Type::Entity && node.type2 == Type::BoundingVolume) {
if (t1 < t2) {
location = (ray.direction * t1) + ray.origin;
t = t1;
object_index = node.index1;
return true;
res.location = (ray.direction * t1) + ray.origin;
res.t = t1;
res.object_index = node.index1;
res.side = side1;
res.hit = true;
return res;
}
else
return RaycastTreeNode(ray, bvh_.at(node.index2), location, t, object_index);
return RaycastTreeNode(ray, bvh_.at(node.index2));
}
// if 1 is a BV and 2 is a BV
if (node.type1 == Type::BoundingVolume && node.type2 == Type::BoundingVolume) {
float node1_t{};
glm::vec3 location1{};
Entity object_index1{};
bool node1_intersects = RaycastTreeNode(ray, bvh_.at(node.index1), location1, node1_t, object_index1);
float node2_t{};
glm::vec3 location2{};
Entity object_index2;
bool node2_intersects = RaycastTreeNode(ray, bvh_.at(node.index2), location2, node2_t, object_index2);
if (node1_intersects && node2_intersects) {
if (node1_t < node2_t) {
location = location1;
t = node1_t;
object_index = object_index1;
return true;
auto node1_intersection = RaycastTreeNode(ray, bvh_.at(node.index1));
auto node2_intersection = RaycastTreeNode(ray, bvh_.at(node.index2));
if (node1_intersection.hit && node2_intersection.hit) {
if (node1_intersection.t < node2_intersection.t) {
res.location = node1_intersection.location;
res.t = node1_intersection.t;
res.object_index = node1_intersection.object_index;
res.side = node1_intersection.side;
res.hit = true;
return res;
}
else {
location = location2;
t = node2_t;
object_index = object_index1;
return true;
res.location = node2_intersection.location;
res.t = node2_intersection.t;
res.object_index = node2_intersection.object_index;
res.side = node2_intersection.side;
res.hit = true;
return res;
}
}
else if (node1_intersects) {
t = node1_t;
location = location1;
object_index = object_index1;
return true;
else if (node1_intersection.hit) {
res.location = node1_intersection.location;
res.t = node1_intersection.t;
res.object_index = node1_intersection.object_index;
res.side = node1_intersection.side;
res.hit = true;
return res;
}
else if (node2_intersects) {
t = node2_t;
location = location2;
object_index = object_index2;
return true;
else if (node2_intersection.hit) {
res.location = node2_intersection.location;
res.t = node2_intersection.t;
res.object_index = node2_intersection.object_index;
res.side = node2_intersection.side;
res.hit = true;
return res;
}
else {
return false;
res.hit = false;
return res;
}
}
// if 1 is a gameobject and 2 is a gameobject
if (node.type1 == Type::Entity && node.type2 == Type::Entity) {
if (t1 < t2) {
location = (ray.direction * t1) + ray.origin;
t = t1;
object_index = node.index1;
res.location = (ray.direction * t1) + ray.origin;
res.t = t1;
res.object_index = node.index1;
res.side = side1;
res.hit = true;
return res;
}
else {
location = (ray.direction * t2) + ray.origin;
t = t2;
object_index = node.index2;
}
return true;
res.location = (ray.direction * t2) + ray.origin;
res.t = t2;
res.object_index = node.index2;
res.side = side2;
res.hit = true;
return res;
}
}
// only 1 hits
if (is_hit1) {
}
else if (is_hit1) {
switch (node.type1) {
case Type::BoundingVolume:
return RaycastTreeNode(ray, bvh_.at(node.index1), location, t, object_index);
return RaycastTreeNode(ray, bvh_.at(node.index1));
case Type::Entity:
location = (ray.direction * t1) + ray.origin;
t = t1;
object_index = node.index1;
return true;
res.location = (ray.direction * t1) + ray.origin;
res.t = t1;
res.object_index = node.index1;
res.side = side1;
res.hit = true;
return res;
}
}
else if (is_hit2) {
switch (node.type2) {
case Type::BoundingVolume:
return RaycastTreeNode(ray, bvh_.at(node.index2));
case Type::Entity:
res.location = (ray.direction * t2) + ray.origin;
res.t = t2;
res.object_index = node.index2;
res.side = side2;
res.hit = true;
return res;
}
}
// only 2 hits
if (is_hit2) {
switch (node.type2) {
case Type::BoundingVolume:
return RaycastTreeNode(ray, bvh_.at(node.index2), location, t, object_index);
case Type::Entity:
location = (ray.direction * t2) + ray.origin;
t = t2;
object_index = node.index2;
return true;
}
}
throw std::runtime_error("RaycastTreeNode() hit end of function. This should not be possible");
}
} // namespace engine

View File

@ -209,28 +209,23 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
for (const tg::Material& material : model.materials) {
if (material.alphaMode != "OPAQUE") {
LOG_WARN("Material {} contains alphaMode {} which isn't supported yet", material.name, material.alphaMode);
LOG_WARN("Material will be opaque");
}
if (material.doubleSided == true) {
LOG_WARN("Material {} specifies double-sided mesh rendering which isn't supported yet", material.name);
LOG_WARN("Material will be single-sided.");
}
if (material.emissiveTexture.index != -1 || material.emissiveFactor[0] != 0.0 || material.emissiveFactor[1] != 0.0 ||
material.emissiveFactor[2] != 0.0) {
LOG_WARN("Material {} contains an emissive texture or non-zero emissive factor. Emission is currently unsupported.", material.name);
LOG_WARN("Material will be created without emission.");
}
const auto& baseColorFactor4 = material.pbrMetallicRoughness.baseColorFactor;
if (baseColorFactor4[0] != 1.0 || baseColorFactor4[1] != 1.0 || baseColorFactor4[2] != 1.0 || baseColorFactor4[3] != 1.0) {
if (material.pbrMetallicRoughness.baseColorTexture.index != -1) {
LOG_WARN("Material {} contains a base color multiplier which isn't supported yet.", material.name);
LOG_WARN("The material's base color texture will be used as-is.");
}
}
if (material.pbrMetallicRoughness.metallicFactor != 1.0 || material.pbrMetallicRoughness.roughnessFactor != 1.0) {
if (material.pbrMetallicRoughness.metallicRoughnessTexture.index != -1) {
LOG_WARN("Material {} contains a metallic and/or roughness multiplier which isn't supported yet.", material.name);
LOG_WARN("The material's metallic-roughness texture will be used as-is.");
}
}
@ -244,7 +239,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
}
else {
LOG_WARN("Material {} base color texture specifies a UV channel other than zero which is unsupported.", material.name);
LOG_WARN("Material will be created with a white base color");
}
}
else if (baseColorFactor4[0] != 1.0 || baseColorFactor4[1] != 1.0 || baseColorFactor4[2] != 1.0 || baseColorFactor4[3] != 1.0) {
@ -271,12 +265,11 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
}
else {
LOG_WARN("Material {} metallic roughness texture specifies a UV channel other than zero which is unsupported.", material.name);
LOG_WARN("Material will be created with a default metallic roughness");
}
}
else {
LOG_INFO("Creating a metallic-roughness texture...");
const std::vector<double> mr_values{1.0f, material.pbrMetallicRoughness.metallicFactor, material.pbrMetallicRoughness.roughnessFactor, 1.0f};
const std::vector<double> mr_values{1.0f, material.pbrMetallicRoughness.roughnessFactor, material.pbrMetallicRoughness.metallicFactor, 1.0f};
Color mr(mr_values);
if (metal_rough_textures.contains(mr) == false) {
const uint8_t pixel[4] = {mr.r, mr.g, mr.b, mr.a};
@ -299,7 +292,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
}
else {
LOG_WARN("Material {} occlusion texture specifies a UV channel other than zero which is unsupported.", material.name);
LOG_WARN("Material will be created with no ambient occlusion");
}
}
@ -311,7 +303,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic)
}
else {
LOG_WARN("Material {} normal texture specifies a UV channel other than zero which is unsupported.", material.name);
LOG_WARN("Material will be created with no normal map");
}
}
}

BIN
test/res/models/MY_AXES.glb Normal file

Binary file not shown.

BIN
test/res/models/stairs.glb Normal file

Binary file not shown.

View File

@ -34,20 +34,17 @@ void CameraControllerSystem::OnUpdate(float ts)
const float dt = ts;
float dx = scene_->app()->input_manager()->GetAxis("movex");
float dy = scene_->app()->input_manager()->GetAxis("movey");
float dx = scene_->app()->input_manager()->GetAxis("movex") * CameraControllerComponent::kSpeedStrafe;
float dy = scene_->app()->input_manager()->GetAxis("movey") * CameraControllerComponent::kSpeedForwardBack;
// calculate new pitch and yaw
constexpr float kMaxPitch = glm::pi<float>();
constexpr float kMinPitch = 0.0f;
float d_pitch = scene_->app()->input_manager()->GetAxis("looky") * -1.0f * c->kCameraSensitivity;
float d_pitch = scene_->app()->input_manager()->GetAxis("looky") * -1.0f * CameraControllerComponent::kCameraSensitivity;
c->pitch += d_pitch;
if (c->pitch <= kMinPitch || c->pitch >= kMaxPitch) {
if (c->pitch <= CameraControllerComponent::kMinPitch || c->pitch >= CameraControllerComponent::kMaxPitch) {
c->pitch -= d_pitch;
}
c->yaw += scene_->app()->input_manager()->GetAxis("lookx") * -1.0f * c->kCameraSensitivity;
c->yaw += scene_->app()->input_manager()->GetAxis("lookx") * -1.0f * CameraControllerComponent::kCameraSensitivity;
// update position relative to camera direction in xz plane
const glm::vec3 d2x_rotated = glm::rotateZ(glm::vec3{dx, 0.0f, 0.0f}, c->yaw);
@ -58,36 +55,81 @@ void CameraControllerSystem::OnUpdate(float ts)
// keep vel.z as gravity can increase it every frame
// gravity stuff here:
constexpr float g = -9.81f; // constant velocity gravity???
constexpr float player_height = 71.0f * 25.4f / 1000.0f;
c->vel.z += g * dt;
c->vel.z += CameraControllerComponent::kGravAccel * dt;
if (scene_->app()->input_manager()->GetButtonPress("jump")) {
// jumping
if (scene_->app()->input_manager()->GetButtonPress("jump") && c->grounded) {
c->vel.z += 4.4f; // m/s
}
// update position with velocity:
// check for collision during next frame and push back and remove velocity in the normal of the collision direction if so
engine::Ray ray{};
ray.origin = t->position;
ray.origin.z -= player_height; // check for collision from the player's feet
// check horizontal collisions first as otherwise the player may be teleported above a wall instead of colliding against it
if (c->vel.x != 0.0f || c->vel.y != 0.0f) { // just in case, to avoid a ray with direction = (0,0,0)
engine::Ray horiz_ray{};
horiz_ray.origin = t->position; // set origin to 'MaxStairHeight' units above player's feet
horiz_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight;
horiz_ray.direction.x = c->vel.x;
horiz_ray.direction.y = c->vel.y; // this is normalized by GetRayCast()
horiz_ray.direction.z = 0.0f;
const engine::Raycast horiz_raycast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(horiz_ray);
if (horiz_raycast.hit) {
const glm::vec2 norm_xy = glm::normalize(glm::vec2{horiz_raycast.normal.x, horiz_raycast.normal.y}) * -1.0f; // make it point towards object
const glm::vec2 vel_xy = glm::vec2{ c->vel.x, c->vel.y };
// find the extent of the player's velocity in the direction of the wall's normal vector
const glm::vec2 partial_vel = norm_xy * glm::dot(norm_xy, vel_xy);
const glm::vec2 partial_dX = partial_vel * dt;
if (glm::length(partial_dX) > horiz_raycast.distance - CameraControllerComponent::kPlayerCollisionRadius) {
// player will collide with wall
// push player out of collision zone
const glm::vec2 push_vector = glm::normalize(vel_xy) * fmaxf(CameraControllerComponent::kPlayerCollisionRadius, horiz_raycast.distance);
t->position.x = horiz_raycast.location.x - push_vector.x;
t->position.y = horiz_raycast.location.y - push_vector.y;
c->vel.x -= partial_vel.x;
c->vel.y -= partial_vel.y;
}
}
}
const glm::vec3 dX = c->vel * dt; // where player will end up with no collision
ray.direction = glm::normalize(dX);
const engine::Raycast raycast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(ray);
if (raycast.hit && raycast.distance < glm::length(dX)) {
// will collide
t->position -= raycast.distance * ray.direction; // push out of collision zone
c->vel.z = 0.0f; // remove velocity normal to collision surface
// check falling collisions
if (c->vel.z < 0.0f) { // if falling
engine::Ray fall_ray{};
fall_ray.origin = t->position;
fall_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight;
fall_ray.direction = {0.0f, 0.0f, -1.0f}; // down
const engine::Raycast fall_raycast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(fall_ray);
if (fall_raycast.hit) { // there is ground below player
// find how far the player will move (downwards) if velocity is applied without collision
const float mag_dz = fabsf(c->vel.z * dt);
// check if the player will be less than 'height' units above the collided ground
if (mag_dz > fall_raycast.distance - CameraControllerComponent::kMaxStairHeight) {
LOG_INFO("HIT");
// push player up to ground level and set as grounded
t->position.z = fall_raycast.location.z + CameraControllerComponent::kPlayerHeight;
c->vel.z = 0.0f;
c->grounded = true;
}
else { // there is ground below player, however they are not grounded
c->grounded = false;
}
}
else { // there is no ground below player (THEY ARE FALLING INTO THE VOID)
c->grounded = false;
}
}
else if (c->vel.z > 0.0f) {
c->grounded = false; // they are jumping
}
if (c->was_grounded != c->grounded) {
LOG_INFO("GROUNDED? {}", c->grounded);
c->was_grounded = c->grounded;
}
t->position += c->vel * dt;
constexpr float kMaxDistanceFromOrigin = 10000.0f;
if (glm::length(t->position) > kMaxDistanceFromOrigin) {
t->position = {0.0f, 0.0f, 10.0f};
if (glm::length(t->position) > CameraControllerComponent::kMaxDistanceFromOrigin) {
t->position = {0.0f, 0.0f, 100.0f};
}
/* ROTATION STUFF */
@ -122,6 +164,9 @@ void CameraControllerSystem::OnUpdate(float ts)
if (scene_->app()->window()->GetKeyPress(engine::inputs::Key::K_R)) {
t->position = {0.0f, 0.0f, 10.0f};
c->vel = {0.0f, 0.0f, 0.0f};
c->pitch = glm::half_pi<float>();
c->yaw = 0.0f;
}
if (scene_->app()->input_manager()->GetButtonPress("fullscreen")) {
@ -136,14 +181,38 @@ void CameraControllerSystem::OnUpdate(float ts)
scene_->app()->scene_manager()->SetActiveScene(next_scene_);
}
if (scene_->app()->window()->GetButtonPress(engine::inputs::MouseButton::M_LEFT)) {
static std::vector<engine::Line> perm_lines{};
if (scene_->app()->window()->GetButton(engine::inputs::MouseButton::M_LEFT)) {
engine::Ray ray{};
ray.origin = t->position;
ray.direction = glm::vec3(glm::mat4_cast(t->rotation) * glm::vec4{0.0f, 0.0f, -1.0f, 1.0f});
engine::Raycast cast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(ray);
if (cast.hit) {
LOG_INFO("Raycast hit {}", scene_->GetComponent<engine::TransformComponent>(cast.hit_entity)->tag);
LOG_INFO("Distance: {} m", cast.distance);
LOG_INFO("Location: {} {} {}", cast.location.x, cast.location.y, cast.location.z);
LOG_INFO("Normal: {} {} {}", cast.normal.x, cast.normal.y, cast.normal.z);
LOG_INFO("Ray direction: {} {} {}", ray.direction.x, ray.direction.y, ray.direction.z);
LOG_INFO("Hit Entity: {}", scene_->GetComponent<engine::TransformComponent>(cast.hit_entity)->tag);
perm_lines.emplace_back(ray.origin, cast.location, glm::vec3{0.0f, 0.0f, 1.0f});
}
}
if (scene_->app()->window()->GetButtonPress(engine::inputs::MouseButton::M_RIGHT)) {
engine::Ray horiz_ray{};
horiz_ray.origin = t->position; // set origin to 'MaxStairHeight' units above player's feet
horiz_ray.origin.z += CameraControllerComponent::kMaxStairHeight - CameraControllerComponent::kPlayerHeight;
horiz_ray.direction.x = c->vel.x;
horiz_ray.direction.y = c->vel.y; // this is normalized by GetRayCast()
horiz_ray.direction.z = 0.0f;
const engine::Raycast cast = scene_->GetSystem<engine::CollisionSystem>()->GetRaycast(horiz_ray);
if (cast.hit) {
LOG_INFO("Distance: {} m", cast.distance);
LOG_INFO("Location: {} {} {}", cast.location.x, cast.location.y, cast.location.z);
LOG_INFO("Normal: {} {} {}", cast.normal.x, cast.normal.y, cast.normal.z);
perm_lines.emplace_back(horiz_ray.origin, cast.location, glm::vec3{ 0.0f, 0.0f, 1.0f });
}
}
scene_->app()->debug_lines.insert(scene_->app()->debug_lines.end(), perm_lines.begin(), perm_lines.end());
}

View File

@ -7,11 +7,21 @@
#include "ecs.h"
struct CameraControllerComponent {
static constexpr float kWalkSpeed = 4.0f;
static constexpr float kSpeedForwardBack = 4.0f;
static constexpr float kSpeedStrafe = 4.0f;
static constexpr float kCameraSensitivity = 0.007f;
static constexpr float kMaxDistanceFromOrigin = 10000.0f;
static constexpr float kGravAccel = -9.81f;
static constexpr float kPlayerHeight = 71.0f * 25.4f / 1000.0f;
static constexpr float kMaxStairHeight = 0.2f;
static constexpr float kPlayerCollisionRadius = 0.2f;
static constexpr float kMaxPitch = glm::pi<float>();
static constexpr float kMinPitch = 0.0f;
float yaw = 0.0f;
float pitch = glm::half_pi<float>();
glm::vec3 vel{ 0.0f, 0.0f, 0.0f };
bool grounded = false;
bool was_grounded = false;
};
class CameraControllerSystem

View File

@ -127,18 +127,20 @@ void PlayGame(GameSettings settings)
skybox_renderable->material->SetAlbedoTexture(app.GetResource<engine::Texture>("builtin.black"));
engine::Entity helmet = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/DamagedHelmet.glb"));
main_scene->GetPosition(helmet) += glm::vec3{20.0f, 10.0f, 5.0f};
main_scene->GetPosition(helmet) += glm::vec3{5.0f, 5.0f, 1.0f};
main_scene->GetScale(helmet) *= 3.0f;
engine::Entity toycar = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/ToyCar.glb"));
main_scene->GetScale(toycar) *= 100.0f;
auto car_spin = main_scene->AddComponent<engine::CustomComponent>(toycar);
car_spin->onInit = []() -> void {};
car_spin->onUpdate = [&](float dt) -> void {
static float yaw = 0.0f;
yaw += dt;
main_scene->GetRotation(toycar) = glm::angleAxis(yaw, glm::vec3{0.0f, 0.0f, 1.0f});
main_scene->GetRotation(toycar) *= glm::angleAxis(glm::half_pi<float>(), glm::vec3{1.0f, 0.0f, 0.0f});
};
main_scene->GetPosition(toycar).z += 1.8f;
engine::Entity stairs = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/stairs.glb"));
main_scene->GetPosition(stairs) += glm::vec3{-8.0f, -5.0f, 0.1f};
main_scene->GetRotation(stairs) = glm::angleAxis(glm::half_pi<float>(), glm::vec3{ 0.0f, 0.0f, 1.0f });
main_scene->GetRotation(stairs) *= glm::angleAxis(glm::half_pi<float>(), glm::vec3{ 1.0f, 0.0f, 0.0f });
engine::Entity axes = engine::util::LoadGLTF(*main_scene, app.GetResourcePath("models/MY_AXES.glb"));
main_scene->GetPosition(axes) += glm::vec3{-40.0f, -40.0f, 1.0f};
}
start_scene->GetSystem<CameraControllerSystem>()->next_scene_ = main_scene;

View File

@ -1 +0,0 @@
This is a test