├── .git-blame-ignore-revs ├── src ├── framework │ ├── ui │ │ ├── ui_utils.cpp │ │ ├── imgui_timeline.cpp │ │ ├── gizmo_2d.h │ │ ├── gizmo_2d.cpp │ │ ├── ui_utils.h │ │ └── io.h │ ├── camera │ │ ├── camera_2d.cpp │ │ ├── camera_2d.h │ │ ├── flyover_camera.h │ │ ├── orbit_camera.h │ │ ├── camera_3d.h │ │ ├── flyover_camera.cpp │ │ ├── orbit_camera.cpp │ │ ├── camera_3d.cpp │ │ └── camera.h │ ├── utils │ │ ├── json_utils.h │ │ ├── timer.h │ │ ├── hash.cpp │ │ ├── json_utils.cpp │ │ ├── timer.cpp │ │ ├── utils.h │ │ └── hash.h │ ├── nodes │ │ ├── camera.cpp │ │ ├── node_binary_format.h │ │ ├── node_factory.cpp │ │ ├── camera.h │ │ ├── environment_3d.h │ │ ├── node_factory.h │ │ ├── skeleton_helper_3d.h │ │ ├── viewport_3d.h │ │ ├── directional_light_3d.h │ │ ├── omni_light_3d.h │ │ ├── joint_3d.h │ │ ├── mesh_instance_3d.h │ │ ├── viewport_3d.cpp │ │ ├── spot_light_3d.h │ │ ├── text_3d.h │ │ ├── skeleton_instance_3d.h │ │ ├── look_at_ik_3d.h │ │ ├── environment_3d.cpp │ │ ├── skeleton_helper_3d.cpp │ │ ├── animation_player.h │ │ ├── node_3d.h │ │ ├── light_3d.h │ │ ├── gs_node.h │ │ ├── slider_2d.h │ │ └── container_2d.h │ ├── parsers │ │ ├── parse_ply.h │ │ ├── parse_vdb.h │ │ ├── parse_obj.h │ │ ├── parse_scene.h │ │ ├── parser.cpp │ │ ├── parse_gltf.h │ │ ├── parse_scene.cpp │ │ └── parser.h │ ├── animation │ │ ├── solvers │ │ │ ├── ccd_solver.h │ │ │ ├── fabrik_solver.h │ │ │ ├── jacobian_solver.h │ │ │ └── ik_solver.h │ │ ├── blend_animation.h │ │ ├── pose.h │ │ ├── skeleton.h │ │ ├── animation.h │ │ ├── pose.cpp │ │ ├── track.h │ │ └── interpolator.h │ ├── colors.h │ ├── math │ │ ├── frustum_cull.h │ │ ├── aabb.h │ │ └── transform.h │ ├── resources │ │ ├── resource.h │ │ └── resource.cpp │ └── input_xr.h ├── graphics │ ├── graphics_utils.h │ ├── backend_include.h │ ├── debug │ │ ├── renderdoc_capture.h │ │ └── renderdoc_capture.cpp │ ├── uniform.h │ ├── primitives │ │ ├── primitive_mesh.h │ │ ├── primitive_mesh.cpp │ │ ├── box_mesh.h │ │ ├── cone_mesh.h │ │ ├── sphere_mesh.h │ │ ├── capsule_mesh.h │ │ ├── torus_mesh.h │ │ ├── quad_mesh.h │ │ ├── cylinder_mesh.h │ │ ├── cone_mesh.cpp │ │ ├── box_mesh.cpp │ │ ├── sphere_mesh.cpp │ │ ├── capsule_mesh.cpp │ │ └── torus_mesh.cpp │ ├── font_common.h │ ├── graphics_utils.cpp │ ├── kernels │ │ ├── prefix_sum_kernel.h │ │ └── radix_sort_kernel.h │ ├── uniforms_structs.h │ ├── font.h │ ├── uniform.cpp │ ├── mesh.h │ ├── hdre.h │ └── pipeline.h ├── .editorconfig ├── engine │ ├── scene_binary_format.h │ ├── scene.h │ └── engine.h ├── includes.h └── xr │ ├── xr_context.cpp │ └── webxr │ └── webxr_context.h ├── docs ├── images │ ├── upf.png │ ├── Node_scheme.png │ ├── logo_maxr_main_sRGB.png │ ├── logo_maxr_main_sRGB_light.png │ └── miciu-cofinanciadoUE-aei.png ├── ui.html ├── xr.html ├── support.html ├── reference │ ├── rendering-pipeline-desc.html │ ├── scene.html │ ├── uniform.html │ ├── mesh.html │ ├── pipeline.html │ ├── shader.html │ └── mesh-instance-3d.html ├── js-bindings.html ├── disk-storage.html └── materials.html ├── .gitignore ├── data └── shaders │ ├── mipmaps.wgsl │ ├── quad_mirror.wgsl │ ├── pbr_material.wgsl │ ├── mesh_includes.wgsl │ ├── AABB_shader.wgsl │ ├── mesh_transparent.wgsl │ ├── kernels │ └── radix_sort_reorder.wgsl │ ├── mesh_shadow.wgsl │ ├── ui │ ├── ui_panel.wgsl │ ├── ui_texture.wgsl │ ├── ui_ray_pointer.wgsl │ ├── ui_group.wgsl │ ├── ui_text_shadow.wgsl │ └── ui_includes.wgsl │ ├── mesh_texture_cube.wgsl │ ├── sdf_fonts.wgsl │ ├── brdf_lut_gen.wgsl │ ├── tonemappers.wgsl │ ├── gaussian_splatting │ ├── gs_render.wgsl │ └── gs_covariance.wgsl │ └── mesh_grid.wgsl ├── LICENSE ├── libraries └── glfw3webgpu │ ├── CMakeLists.txt │ └── glfw3webgpu.hpp ├── .gitmodules └── glsl_builder.py /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # .git-blame-ignore-revs -------------------------------------------------------------------------------- /src/framework/ui/ui_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "ui_utils.h" 2 | -------------------------------------------------------------------------------- /src/framework/ui/imgui_timeline.cpp: -------------------------------------------------------------------------------- 1 | #include "imgui_timeline.h" 2 | -------------------------------------------------------------------------------- /docs/images/upf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upf-gti/wgpuEngine/HEAD/docs/images/upf.png -------------------------------------------------------------------------------- /docs/images/Node_scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upf-gti/wgpuEngine/HEAD/docs/images/Node_scheme.png -------------------------------------------------------------------------------- /docs/images/logo_maxr_main_sRGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upf-gti/wgpuEngine/HEAD/docs/images/logo_maxr_main_sRGB.png -------------------------------------------------------------------------------- /docs/ui.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/images/logo_maxr_main_sRGB_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upf-gti/wgpuEngine/HEAD/docs/images/logo_maxr_main_sRGB_light.png -------------------------------------------------------------------------------- /docs/images/miciu-cofinanciadoUE-aei.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/upf-gti/wgpuEngine/HEAD/docs/images/miciu-cofinanciadoUE-aei.png -------------------------------------------------------------------------------- /src/framework/camera/camera_2d.cpp: -------------------------------------------------------------------------------- 1 | #include "camera_2d.h" 2 | 3 | Camera2D::Camera2D() 4 | { 5 | view = glm::identity(); 6 | } 7 | -------------------------------------------------------------------------------- /src/framework/camera/camera_2d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "camera.h" 4 | 5 | class Camera2D : public Camera { 6 | 7 | public: 8 | 9 | Camera2D(); 10 | }; 11 | -------------------------------------------------------------------------------- /src/framework/utils/json_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rapidjson/fwd.h" 4 | 5 | #include 6 | 7 | rapidjson::Document load_json(const std::string& filename); 8 | -------------------------------------------------------------------------------- /src/graphics/graphics_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/glm.hpp" 4 | 5 | struct WebGPUContext; 6 | 7 | glm::uvec2 find_optimal_dispatch_size(WebGPUContext* webgpu_context, uint32_t workgroup_count); 8 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{h,cpp,hpp}] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | tab_width = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /src/framework/nodes/camera.cpp: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | 3 | #include "framework/nodes/node_factory.h" 4 | 5 | REGISTER_NODE_CLASS(EntityCamera) 6 | 7 | EntityCamera::EntityCamera() : Node3D() 8 | { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/framework/nodes/node_binary_format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr uint8_t MAX_NODE_NAME_SIZE = 64; 6 | 7 | struct sNodeBinaryHeader { 8 | uint64_t children_count = 0; 9 | }; 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build-web/ 3 | .cache/ 4 | .vscode/ 5 | libraries/tinyobjloader/ 6 | libraries/stb/ 7 | libraries/nlohmann_json/ 8 | libraries/tiny_gltf/ 9 | src/xr/dawnxr/ 10 | *.gen.h 11 | .DS_Store 12 | **/.DS_Store -------------------------------------------------------------------------------- /src/engine/scene_binary_format.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr uint8_t MAX_SCENE_NAME_SIZE = 64; 6 | 7 | struct sSceneBinaryHeader { 8 | uint8_t version = 1; 9 | uint64_t node_count = 0; 10 | }; 11 | -------------------------------------------------------------------------------- /src/framework/camera/flyover_camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "camera_3d.h" 4 | 5 | class FlyoverCamera : public Camera3D { 6 | 7 | public: 8 | 9 | FlyoverCamera(); 10 | 11 | void update(float delta_time) override; 12 | }; 13 | -------------------------------------------------------------------------------- /docs/xr.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/framework/parsers/parse_ply.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "parser.h" 4 | 5 | class PlyParser : public Parser { 6 | public: 7 | 8 | bool parse(std::string file_path, std::vector& entities, uint32_t flags = PARSE_DEFAULT) override; 9 | }; 10 | -------------------------------------------------------------------------------- /src/framework/parsers/parse_vdb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "parser.h" 4 | 5 | class VdbParser : public Parser { 6 | public: 7 | 8 | bool parse(std::string file_path, std::vector& entities, uint32_t flags = PARSE_DEFAULT) override; 9 | }; 10 | -------------------------------------------------------------------------------- /src/graphics/backend_include.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(BACKEND_DX12) 4 | #include 5 | #elif defined(BACKEND_VULKAN) 6 | #include "dawn/native/VulkanBackend.h" 7 | #elif defined(BACKEND_METAL) 8 | #include "dawn/native/MetalBackend.h" 9 | #endif -------------------------------------------------------------------------------- /src/framework/parsers/parse_obj.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class MeshInstance3D; 6 | 7 | void parse_obj(const std::string& obj_path, MeshInstance3D* entity_mesh, bool create_aabb = true); 8 | MeshInstance3D* parse_obj(const std::string& obj_path, bool create_aabb = true); 9 | -------------------------------------------------------------------------------- /src/framework/nodes/node_factory.cpp: -------------------------------------------------------------------------------- 1 | #include "node_factory.h" 2 | 3 | #include "node.h" 4 | 5 | #include "assert.h" 6 | 7 | NodeRegistration::NodeRegistration(const std::string& node_name, NodeFactory node_ctor) 8 | { 9 | NodeRegistry::get_instance()->register_class(node_name, node_ctor); 10 | } 11 | -------------------------------------------------------------------------------- /src/framework/nodes/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/nodes/node_3d.h" 4 | #include "framework/camera/camera_3d.h" 5 | 6 | class EntityCamera : public Camera3D, public Node3D { 7 | 8 | protected: 9 | 10 | 11 | public: 12 | 13 | EntityCamera(); 14 | virtual ~EntityCamera() {}; 15 | 16 | // void update(float delta_time) override; 17 | }; 18 | -------------------------------------------------------------------------------- /src/framework/nodes/environment_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mesh_instance_3d.h" 4 | 5 | class Environment3D : public MeshInstance3D { 6 | 7 | public: 8 | 9 | Environment3D(); 10 | virtual ~Environment3D() {}; 11 | 12 | void update(float delta_time) override; 13 | 14 | Texture* get_texture() const; 15 | void set_texture(const std::string& texture_path); 16 | }; 17 | -------------------------------------------------------------------------------- /src/framework/parsers/parse_scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Node; 6 | class Node3D; 7 | class MeshInstance3D; 8 | 9 | bool parse_scene(const char* scene_path, std::vector &entities, bool fill_surface_data = false, Node3D* root = nullptr); 10 | MeshInstance3D* parse_mesh(const char* mesh_path, bool create_aabb = true, bool fill_surface_data = false); 11 | -------------------------------------------------------------------------------- /src/framework/animation/solvers/ccd_solver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ik_solver.h" 4 | 5 | class CCDSolver : public IKSolver { 6 | protected: 7 | 8 | Transform aux_parent; // in global 9 | 10 | public: 11 | 12 | bool solve(const Transform& target) override; 13 | 14 | void apply_ball_socket_constraint(int i, float limit_angle); 15 | void apply_hinge_socket_constraint(int i, const glm::vec3& axis); 16 | }; 17 | -------------------------------------------------------------------------------- /docs/support.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/framework/nodes/node_factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Node; 6 | 7 | typedef Node* (*NodeFactory)(void); 8 | 9 | class NodeRegistration 10 | { 11 | public: 12 | NodeRegistration(const std::string& node_name, NodeFactory node_ctor); 13 | }; 14 | 15 | #define REGISTER_NODE_CLASS(node_ctor) \ 16 | NodeRegistration _module_registration_ ## node_ctor(#node_ctor, []() -> Node* { return new node_ctor(); }); 17 | -------------------------------------------------------------------------------- /src/framework/camera/orbit_camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "camera_3d.h" 4 | 5 | class OrbitCamera : public Camera3D { 6 | 7 | public: 8 | 9 | OrbitCamera(); 10 | 11 | void update(float delta_time) override; 12 | 13 | void look_at(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up, bool reset_internals = true) override; 14 | 15 | LerpedValue distance_lerp; 16 | float distance = 0.0f; 17 | }; 18 | -------------------------------------------------------------------------------- /src/graphics/debug/renderdoc_capture.h: -------------------------------------------------------------------------------- 1 | #ifndef __EMSCRIPTEN__ 2 | #include "renderdoc_app.h" 3 | #endif 4 | 5 | class RenderdocCapture { 6 | 7 | #ifndef __EMSCRIPTEN__ 8 | static RENDERDOC_API_1_6_0* rdoc_api; 9 | #endif 10 | 11 | static bool capture_started; 12 | 13 | public: 14 | 15 | static void init(); 16 | static void start_capture_frame(); 17 | static void end_capture_frame(); 18 | static bool is_capture_started(); 19 | }; 20 | 21 | -------------------------------------------------------------------------------- /src/graphics/uniform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | 5 | #include 6 | #include 7 | 8 | struct Uniform { 9 | 10 | Uniform(); 11 | ~Uniform(); 12 | 13 | void destroy(); 14 | 15 | std::variant data; 16 | 17 | uint32_t binding = 0; 18 | uint64_t buffer_size = 0; 19 | 20 | WGPUBindGroupEntry get_bind_group_entry() const; 21 | }; 22 | -------------------------------------------------------------------------------- /src/framework/nodes/skeleton_helper_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/nodes/mesh_instance_3d.h" 4 | 5 | class Skeleton; 6 | 7 | class SkeletonHelper3D : public MeshInstance3D { 8 | 9 | Skeleton* skeleton = nullptr; 10 | 11 | void inner_update(); 12 | 13 | public: 14 | 15 | SkeletonHelper3D(Skeleton* new_skeleton, Node3D* parent = nullptr); 16 | 17 | virtual void initialize() override; 18 | 19 | void update(float delta_time) override; 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /src/includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if !defined(__EMSCRIPTEN__) 4 | // NOTE: Uncomment this in order to force the XR Support 5 | // Otherwise only available on Windows 6 | //#define XR_SUPPORT 7 | //#define USE_MIRROR_WINDOW 8 | #endif 9 | 10 | #include 11 | 12 | #ifdef __EMSCRIPTEN__ 13 | #include 14 | #endif 15 | 16 | enum eEYE { 17 | EYE_LEFT, 18 | EYE_RIGHT, 19 | EYE_COUNT // Let's assume this will never be different to 2... 20 | }; 21 | -------------------------------------------------------------------------------- /src/graphics/primitives/primitive_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/mesh.h" 4 | 5 | class PrimitiveMesh : public Mesh 6 | { 7 | protected: 8 | 9 | Surface* surface = nullptr; 10 | 11 | glm::vec3 color = {1.0f, 1.0f, 1.0f}; 12 | 13 | bool dirty = false; 14 | 15 | virtual void build_mesh() {} 16 | 17 | public: 18 | 19 | PrimitiveMesh(const glm::vec3& new_color); 20 | 21 | void render_gui() override; 22 | 23 | void set_color(const glm::vec3& new_color); 24 | }; 25 | -------------------------------------------------------------------------------- /src/graphics/font_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/vec2.hpp" 4 | #include "glm/vec3.hpp" 5 | 6 | struct CKerning { 7 | int first; 8 | int second; 9 | int amount; 10 | }; 11 | 12 | struct Character { 13 | int id; 14 | int index; 15 | char character; 16 | glm::vec2 size; 17 | glm::vec2 offset; 18 | int xadvance; 19 | int chnl; 20 | glm::vec2 pos; 21 | int page; 22 | 23 | // Mesh properties 24 | glm::vec3 vertices[6]; 25 | glm::vec2 uvs[6]; 26 | }; 27 | -------------------------------------------------------------------------------- /src/framework/animation/solvers/fabrik_solver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ik_solver.h" 4 | 5 | class FABRIKSolver : public IKSolver { 6 | protected: 7 | std::vector world_chain; 8 | std::vector lengths; 9 | 10 | void ik_chain_to_world(); 11 | void iterate_forward(const glm::vec3& goal); 12 | void iterate_backward(const glm::vec3& base); 13 | void world_to_ik_chain(); 14 | 15 | public: 16 | void resize(size_t new_size) override; 17 | bool solve(const Transform& target) override; 18 | }; 19 | -------------------------------------------------------------------------------- /src/framework/ui/gizmo_2d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/mat4x4.hpp" 4 | 5 | #include "imgui.h" 6 | #include "framework/utils/ImGuizmo.h" 7 | 8 | class Gizmo2D { 9 | #ifdef __EMSCRIPTEN__ 10 | public: 11 | #endif 12 | 13 | ImGuizmo::OPERATION operation = ImGuizmo::TRANSLATE; 14 | ImGuizmo::MODE mode = ImGuizmo::WORLD; 15 | 16 | public: 17 | 18 | void set_mode(ImGuizmo::MODE new_mode); 19 | void set_operation(ImGuizmo::OPERATION new_operation); 20 | 21 | bool render(const glm::mat4x4& m_view, const glm::mat4x4& m_proj, glm::mat4x4& m_model); 22 | }; 23 | -------------------------------------------------------------------------------- /src/framework/utils/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Timer { 6 | std::chrono::high_resolution_clock::time_point begin; 7 | 8 | template 9 | double get_elapsed_time() 10 | { 11 | std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); 12 | return std::chrono::duration(end - begin).count(); 13 | } 14 | 15 | public: 16 | 17 | void start(); 18 | 19 | void print_elapsed_time_s(); 20 | void print_elapsed_time_ms(); 21 | void print_elapsed_time_ns(); 22 | }; 23 | -------------------------------------------------------------------------------- /src/graphics/graphics_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "graphics_utils.h" 2 | 3 | #include "webgpu_context.h" 4 | 5 | glm::uvec2 find_optimal_dispatch_size(WebGPUContext* webgpu_context, uint32_t workgroup_count) 6 | { 7 | glm::uvec2 dispatch_size = { workgroup_count, 1 }; 8 | 9 | if (workgroup_count > webgpu_context->supported_limits.maxComputeWorkgroupsPerDimension) { 10 | uint32_t x = static_cast(floor(sqrt(workgroup_count))); 11 | uint32_t y = static_cast(ceil(workgroup_count / x)); 12 | 13 | dispatch_size.x = x; 14 | dispatch_size.y = y; 15 | } 16 | 17 | return dispatch_size; 18 | } 19 | -------------------------------------------------------------------------------- /src/framework/animation/solvers/jacobian_solver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ik_solver.h" 4 | 5 | class JacobianSolver : public IKSolver { 6 | 7 | std::vector local_axis_x_joint; 8 | std::vector local_axis_y_joint; 9 | std::vector local_axis_z_joint; 10 | 11 | public: 12 | 13 | float amount = 0.05f; 14 | 15 | Transform& operator[](uint32_t index); 16 | 17 | void add_revolute_joint(const Transform& joint_transform); 18 | 19 | void set_chain(const std::vector& chain); 20 | void set_rotation_axis(); 21 | 22 | bool solve(const Transform& target) override; 23 | }; 24 | -------------------------------------------------------------------------------- /src/framework/colors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/vec4.hpp" 4 | 5 | typedef glm::vec4 Color; 6 | 7 | namespace colors { 8 | const Color WHITE = Color(1.f, 1.f, 1.f, 1.0f); 9 | const Color BLACK = Color(0.f, 0.f, 0.f, 1.0f); 10 | const Color RED = Color(1.f, 0.f, 0.f, 1.0f); 11 | const Color GREEN = Color(0.f, 1.f, 0.f, 1.0f); 12 | const Color BLUE = Color(0.f, 0.f, 1.f, 1.0f); 13 | const Color PURPLE = Color(1.f, 0.f, 1.f, 1.0f); 14 | const Color YELLOW = Color(1.f, 1.f, 0.f, 1.0f); 15 | const Color CYAN = Color(0.f, 1.f, 1.f, 1.0f); 16 | const Color GRAY = Color(0.4f, 0.4f, 0.4f, 1.0f); 17 | const Color RUST = Color(0.82f, 0.35f, 0.15f, 1.0f); 18 | } 19 | -------------------------------------------------------------------------------- /src/framework/nodes/viewport_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mesh_instance_3d.h" 4 | #include "graphics/material.h" 5 | #include "graphics/surface.h" 6 | 7 | class Node2D; 8 | 9 | /* 10 | * This 3D Node allows to use a 2D UI in 3D 11 | */ 12 | 13 | class Viewport3D : public Node3D { 14 | 15 | bool active = true; 16 | 17 | Node2D* root = nullptr; 18 | 19 | glm::vec2 viewport_size = glm::vec2(0.0f); 20 | 21 | public: 22 | 23 | Viewport3D(Node2D* root_2d); 24 | ~Viewport3D(); 25 | 26 | virtual void update(float delta_time) override; 27 | 28 | void set_viewport_size(const glm::vec2& new_size); 29 | void set_active(bool value) { active = value; } 30 | }; 31 | -------------------------------------------------------------------------------- /src/framework/camera/camera_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "camera.h" 4 | 5 | class Node3D; 6 | 7 | class Camera3D : public Camera { 8 | 9 | public: 10 | 11 | Camera3D() = default; 12 | 13 | void apply_movement(const glm::vec2& movement); 14 | 15 | virtual void update(float delta_time); 16 | 17 | virtual void look_at(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up, bool reset_internals = true); 18 | 19 | void look_at_node(Node3D* entity); 20 | 21 | protected: 22 | 23 | float delta_yaw = 0.0f; 24 | float delta_pitch = 0.0f; 25 | 26 | LerpedValue delta_yaw_lerp; 27 | LerpedValue delta_pitch_lerp; 28 | LerpedValue eye_lerp; 29 | }; 30 | -------------------------------------------------------------------------------- /src/graphics/primitives/primitive_mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "primitive_mesh.h" 2 | 3 | #include "imgui.h" 4 | 5 | PrimitiveMesh::PrimitiveMesh(const glm::vec3& new_color) 6 | : color(new_color) 7 | { 8 | mesh_type = ""; 9 | 10 | surface = new Surface(); 11 | 12 | add_surface(surface); 13 | } 14 | 15 | void PrimitiveMesh::set_color(const glm::vec3& new_color) 16 | { 17 | color = new_color; 18 | 19 | build_mesh(); 20 | } 21 | 22 | void PrimitiveMesh::render_gui() 23 | { 24 | ImGui::Text("Color"); 25 | ImGui::SameLine(200); 26 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); 27 | if (ImGui::ColorEdit3("##Color", &color.x)) { 28 | dirty = true; 29 | } 30 | 31 | Mesh::render_gui(); 32 | } 33 | -------------------------------------------------------------------------------- /src/framework/nodes/directional_light_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "light_3d.h" 4 | 5 | class MeshInstance3D; 6 | class Material; 7 | 8 | class DirectionalLight3D : public Light3D { 9 | 10 | MeshInstance3D* debug_mesh_h = nullptr; 11 | MeshInstance3D* debug_mesh_v = nullptr; 12 | 13 | Material* debug_material = nullptr; 14 | 15 | public: 16 | 17 | DirectionalLight3D(); 18 | ~DirectionalLight3D(); 19 | 20 | virtual void render() override; 21 | 22 | void render_gui() override; 23 | 24 | void set_color(const glm::vec3& color) override; 25 | 26 | void get_uniform_data(sLightUniformData& data) override; 27 | 28 | void parse(std::ifstream& binary_scene_file) override; 29 | 30 | void create_debug_meshes() override; 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /src/framework/nodes/omni_light_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "light_3d.h" 4 | 5 | class MeshInstance3D; 6 | class Material; 7 | 8 | class OmniLight3D : public Light3D { 9 | 10 | MeshInstance3D* debug_mesh_h = nullptr; 11 | MeshInstance3D* debug_mesh_v = nullptr; 12 | 13 | Material* debug_material = nullptr; 14 | 15 | public: 16 | 17 | OmniLight3D(); 18 | ~OmniLight3D(); 19 | 20 | virtual void render() override; 21 | 22 | void render_gui() override; 23 | 24 | void set_color(const glm::vec3& color) override; 25 | void set_range(float value) override; 26 | 27 | void get_uniform_data(sLightUniformData& data) override; 28 | 29 | void parse(std::ifstream& binary_scene_file) override; 30 | 31 | void create_debug_meshes() override; 32 | }; 33 | -------------------------------------------------------------------------------- /src/framework/ui/gizmo_2d.cpp: -------------------------------------------------------------------------------- 1 | #include "gizmo_2d.h" 2 | 3 | #include "graphics/renderer.h" 4 | 5 | #include "framework/input.h" 6 | #include "framework/camera/camera.h" 7 | 8 | #include 9 | 10 | void Gizmo2D::set_mode(ImGuizmo::MODE new_mode) 11 | { 12 | this->mode = new_mode; 13 | } 14 | 15 | void Gizmo2D::set_operation(ImGuizmo::OPERATION new_operation) 16 | { 17 | this->operation = new_operation; 18 | } 19 | 20 | bool Gizmo2D::render(const glm::mat4x4& m_view, const glm::mat4x4& m_proj, glm::mat4x4& m_model) 21 | { 22 | const ImGuiIO& io = ImGui::GetIO(); 23 | ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y); 24 | 25 | return ImGuizmo::Manipulate(glm::value_ptr(m_view), glm::value_ptr(m_proj), operation, mode, glm::value_ptr(m_model)); 26 | } 27 | -------------------------------------------------------------------------------- /src/graphics/primitives/box_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/primitives/primitive_mesh.h" 4 | 5 | class BoxMesh : public PrimitiveMesh 6 | { 7 | float width = 1.0f; 8 | float height = 1.0f; 9 | float depth = 1.0f; 10 | 11 | void build_mesh() override; 12 | 13 | public: 14 | 15 | BoxMesh(float width = 1.f, float height = 1.f, float depth = 1.f, const glm::vec3& color = { 1.f, 1.f, 1.f }); 16 | 17 | void render_gui() override; 18 | 19 | float get_width() const { return width; } 20 | float get_height() const { return height; } 21 | float get_depth() const { return depth; } 22 | 23 | void set_width(float new_width); 24 | void set_height(float new_height); 25 | void set_depth(float new_depth); 26 | void set_size(const glm::vec3& size); 27 | }; 28 | -------------------------------------------------------------------------------- /src/graphics/primitives/cone_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/primitives/primitive_mesh.h" 4 | 5 | class ConeMesh : public PrimitiveMesh 6 | { 7 | float radius = 0.5f; 8 | float height = 2.0f; 9 | 10 | uint32_t segments = 64u; 11 | 12 | void build_mesh() override; 13 | 14 | public: 15 | 16 | ConeMesh(float radius = 0.5f, float height = 2.0f, uint32_t segments = 64u, const glm::vec3& color = { 1.f, 1.f, 1.f }); 17 | 18 | void render_gui() override; 19 | 20 | float get_radius() const { return radius; } 21 | float get_height() const { return height; } 22 | uint32_t get_segments() const { return segments; } 23 | 24 | void set_radius(float new_radius); 25 | void set_height(float new_height); 26 | void set_segments(uint32_t new_segments); 27 | }; 28 | -------------------------------------------------------------------------------- /src/framework/parsers/parser.cpp: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | 3 | #include 4 | #include 5 | 6 | std::vector Parser::async_parsers; 7 | 8 | void Parser::poll_async_parsers() 9 | { 10 | std::vector::iterator it = async_parsers.begin(); 11 | while (it != async_parsers.end()) 12 | { 13 | Parser* parser = *it; 14 | if (parser->async_future.valid() && parser->async_future.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) { 15 | 16 | parser->on_async_finished(); 17 | 18 | parser->async_callback(parser->async_nodes, parser->async_future.get()); 19 | 20 | delete* it; 21 | it = async_parsers.erase(it); 22 | } 23 | else { 24 | it++; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/engine/scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Node; 7 | 8 | class Scene { 9 | 10 | std::vector nodes; 11 | 12 | std::string name; 13 | 14 | public: 15 | Scene(); 16 | Scene(const std::string& name); 17 | ~Scene(); 18 | 19 | void add_node(Node* node, int idx = -1); 20 | void add_nodes(const std::vector& nodes_to_add, int idx = -1); 21 | 22 | void remove_node(Node* node); 23 | 24 | void set_name(const std::string& name); 25 | 26 | std::vector& get_nodes(); 27 | const std::string& get_name() const { return name; } 28 | 29 | void delete_all(); 30 | 31 | void serialize(const std::string& path); 32 | void parse(const std::string& path); 33 | 34 | void update(float delta_time); 35 | void render(); 36 | }; 37 | -------------------------------------------------------------------------------- /src/graphics/primitives/sphere_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/primitives/primitive_mesh.h" 4 | 5 | class SphereMesh : public PrimitiveMesh 6 | { 7 | float radius = 0.5f; 8 | 9 | uint32_t rings = 32u; 10 | uint32_t ring_segments = 64u; 11 | 12 | void build_mesh() override; 13 | 14 | public: 15 | 16 | SphereMesh(float radius = 0.5f, uint32_t rings = 32u, uint32_t ring_segments = 64u, const glm::vec3& color = { 1.f, 1.f, 1.f }); 17 | 18 | void render_gui() override; 19 | 20 | float get_radius() const { return radius; } 21 | uint32_t get_rings() const { return rings; } 22 | uint32_t get_ring_segments() const { return ring_segments; } 23 | 24 | void set_radius(float new_radius); 25 | void set_rings(uint32_t new_rings); 26 | void set_ring_segments(uint32_t new_ring_segments); 27 | }; 28 | -------------------------------------------------------------------------------- /src/framework/nodes/joint_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/nodes/node_3d.h" 4 | 5 | class Pose; 6 | class SkeletonInstance3D; 7 | class MeshInstance3D; 8 | 9 | class Joint3D : public Node3D { 10 | 11 | uint32_t index = 0; 12 | Pose* pose = nullptr; 13 | 14 | MeshInstance3D* mesh_instance = nullptr; 15 | MeshInstance3D* selected_mesh_instance = nullptr; 16 | 17 | public: 18 | 19 | static Joint3D* selected_joint; 20 | 21 | Joint3D(); 22 | ~Joint3D(); 23 | 24 | Transform get_global_transform() override; 25 | uint32_t get_index() { return index; } 26 | 27 | void set_index(int32_t new_index) { index = new_index; } 28 | void set_pose(Pose* ref_pose) { pose = ref_pose; }; 29 | void set_global_transform(const Transform& new_transform) override; 30 | 31 | void render() override; 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /src/framework/utils/hash.cpp: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | 3 | bool RenderPipelineKey::operator==(const RenderPipelineKey& other) const 4 | { 5 | return (shader == other.shader 6 | && color_target.format == other.color_target.format 7 | && color_target.writeMask == other.color_target.writeMask 8 | && color_target.blend == other.color_target.blend 9 | && description.cull_mode == other.description.cull_mode 10 | && description.topology == other.description.topology 11 | && description.depth_read == other.description.depth_read 12 | && description.depth_write == other.description.depth_write 13 | && description.blending_enabled == other.description.blending_enabled 14 | && description.depth_compare == other.description.depth_compare 15 | && pipeline_layout == other.pipeline_layout); 16 | } 17 | -------------------------------------------------------------------------------- /data/shaders/mipmaps.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var previousMipLevel: texture_2d; 2 | 3 | #ifdef RGBA8_UNORM 4 | @group(0) @binding(1) var nextMipLevel: texture_storage_2d; 5 | #endif 6 | 7 | #ifdef RGBA32_FLOAT 8 | @group(0) @binding(1) var nextMipLevel: texture_storage_2d; 9 | #endif 10 | 11 | @group(0) @binding(2) var texture_sampler : sampler; 12 | 13 | // https://www.gamedev.net/forums/topic/709862-downsampling-image-in-compute-shader/ 14 | @compute @workgroup_size(8, 8) 15 | fn compute(@builtin(global_invocation_id) id: vec3) { 16 | 17 | let dim : vec2u = textureDimensions(nextMipLevel).xy; 18 | 19 | let uv : vec2f = (vec2f(id.xy) + vec2f(0.5)) / vec2f(dim); 20 | let color : vec4f = textureSampleLevel(previousMipLevel, texture_sampler, uv, 0.0f); 21 | 22 | textureStore(nextMipLevel, id.xy, color); 23 | } -------------------------------------------------------------------------------- /src/xr/xr_context.cpp: -------------------------------------------------------------------------------- 1 | #include "xr_context.h" 2 | 3 | XRContext::~XRContext() 4 | { 5 | } 6 | 7 | uint32_t XRContext::get_swapchain_image_index(uint8_t eye_idx) 8 | { 9 | return 0; 10 | } 11 | 12 | void XRContext::init_frame() 13 | { 14 | } 15 | 16 | void XRContext::acquire_swapchain(int swapchain_index) 17 | { 18 | } 19 | 20 | void XRContext::release_swapchain(int swapchain_index) 21 | { 22 | } 23 | 24 | void XRContext::end_frame() 25 | { 26 | } 27 | 28 | uint32_t XRContext::get_num_images_per_swapchain() 29 | { 30 | return 2; 31 | } 32 | 33 | glm::mat4x4 XrInputPose_to_glm(const XrInputPose& p) { 34 | glm::mat4 translation = glm::translate(glm::mat4{ 1.f }, glm::vec3(p.position.x, p.position.y, p.position.z)); 35 | glm::mat4 orientation = glm::mat4_cast(glm::quat(p.orientation.x, p.orientation.y, p.orientation.z, p.orientation.w)); 36 | return translation * orientation; 37 | } 38 | -------------------------------------------------------------------------------- /src/framework/utils/json_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "json_utils.h" 2 | 3 | #include 4 | 5 | #include "spdlog/spdlog.h" 6 | 7 | #include "rapidjson/document.h" 8 | #include "rapidjson/istreamwrapper.h" 9 | 10 | rapidjson::Document load_json(const std::string& filename) { 11 | 12 | rapidjson::Document j; 13 | 14 | while (true) { 15 | 16 | std::ifstream ifs(filename.c_str()); 17 | if (!ifs.is_open()) { 18 | spdlog::error("Failed to open json file {}", filename); 19 | continue; 20 | } 21 | 22 | rapidjson::IStreamWrapper isw(ifs); 23 | rapidjson::Document document; 24 | 25 | j.ParseStream(isw); 26 | if (j.HasParseError()) { 27 | ifs.close(); 28 | spdlog::error("Failed to parse json file {}", filename); 29 | continue; 30 | } 31 | 32 | // The json is correct, we can leave the while loop 33 | break; 34 | } 35 | 36 | return j; 37 | } 38 | -------------------------------------------------------------------------------- /src/framework/utils/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | 3 | #include "spdlog/spdlog.h" 4 | 5 | void Timer::start() 6 | { 7 | begin = std::chrono::steady_clock::now(); 8 | } 9 | 10 | void Timer::print_elapsed_time_s() 11 | { 12 | if (begin == std::chrono::steady_clock::time_point()) { 13 | spdlog::error("Timer was not started!"); 14 | } 15 | 16 | spdlog::info("Time elapsed: {} [s]", get_elapsed_time>()); 17 | } 18 | 19 | void Timer::print_elapsed_time_ms() 20 | { 21 | if (begin == std::chrono::steady_clock::time_point()) { 22 | spdlog::error("Timer was not started!"); 23 | } 24 | 25 | spdlog::info("Time elapsed: {} [ms]", get_elapsed_time()); 26 | } 27 | 28 | void Timer::print_elapsed_time_ns() 29 | { 30 | if (begin == std::chrono::steady_clock::time_point()) { 31 | spdlog::error("Timer was not started!"); 32 | } 33 | 34 | spdlog::info("Time elapsed: {} [ns]", get_elapsed_time()); 35 | } 36 | -------------------------------------------------------------------------------- /data/shaders/quad_mirror.wgsl: -------------------------------------------------------------------------------- 1 | #include mesh_includes.wgsl 2 | 3 | #define GAMMA_CORRECTION 4 | 5 | @vertex 6 | fn vs_main(in: VertexInput) -> VertexOutput { 7 | var out: VertexOutput; 8 | out.position = vec4f(in.position, 1.0); 9 | out.uv = in.uv; // forward to the fragment shader 10 | return out; 11 | } 12 | 13 | @group(0) @binding(0) var left_eye_texture: texture_2d; 14 | @group(0) @binding(1) var texture_sampler : sampler; 15 | 16 | struct FragmentOutput { 17 | @location(0) color: vec4f 18 | } 19 | 20 | @fragment 21 | fn fs_main(in: VertexOutput) -> FragmentOutput { 22 | 23 | let uvs : vec2f = vec2f(in.uv.x, in.uv.y); 24 | let xr_image = textureSample(left_eye_texture, texture_sampler, uvs); 25 | 26 | var out: FragmentOutput; 27 | 28 | if (GAMMA_CORRECTION == 0) { 29 | out.color = vec4f(pow(xr_image.rgb, 1.0 / vec3f(2.2)), 1.0); 30 | } else { 31 | out.color = vec4f(xr_image.rgb, 1.0); 32 | } 33 | 34 | return out; 35 | } 36 | -------------------------------------------------------------------------------- /src/graphics/primitives/capsule_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/primitives/primitive_mesh.h" 4 | 5 | class CapsuleMesh : public PrimitiveMesh 6 | { 7 | float radius = 0.5f; 8 | float height = 2.0f; 9 | 10 | uint32_t rings = 8u; 11 | uint32_t ring_segments = 64u; 12 | 13 | void build_mesh() override; 14 | 15 | public: 16 | 17 | CapsuleMesh(float radius = 0.5f, float height = 2.0f, uint32_t rings = 8u, uint32_t ring_segments = 64u, const glm::vec3& color = { 1.f, 1.f, 1.f }); 18 | 19 | void render_gui() override; 20 | 21 | float get_radius() const { return radius; } 22 | float get_height() const { return height; } 23 | uint32_t get_rings() const { return rings; } 24 | uint32_t get_ring_segments() const { return ring_segments; } 25 | 26 | void set_radius(float new_ring_radius); 27 | void set_height(float new_height); 28 | void set_rings(uint32_t new_rings); 29 | void set_ring_segments(uint32_t new_ring_segments); 30 | }; 31 | -------------------------------------------------------------------------------- /data/shaders/pbr_material.wgsl: -------------------------------------------------------------------------------- 1 | struct PbrMaterial 2 | { 3 | pos : vec3f, 4 | normal_g : vec3f, 5 | normal : vec3f, 6 | albedo : vec3f, 7 | emissive : vec3f, 8 | f0 : vec3f, 9 | f0_dielectric : vec3f, 10 | f90 : vec3f, 11 | ior: f32, 12 | diffuse : vec3f, 13 | specular_weight: f32, 14 | metallic : f32, 15 | roughness : f32, 16 | ao : f32, 17 | 18 | // KHR_materials_clearcoat 19 | clearcoat_f0 : vec3f, 20 | clearcoat_f90 : vec3f, 21 | clearcoat_factor : f32, 22 | clearcoat_normal: vec3f, 23 | clearcoat_roughness : f32, 24 | clearcoat_fresnel: vec3f, 25 | 26 | // KHR_materials_iridescence 27 | iridescence_factor : f32, 28 | iridescence_ior : f32, 29 | iridescence_thickness : f32, 30 | 31 | // KHR_materials_anisotropy 32 | anisotropy_factor : f32, 33 | anisotropy_tangent : vec3f, 34 | anisotropy_bitangent : vec3f, 35 | 36 | n_dot_v : f32, 37 | view_dir : vec3f, 38 | reflected_dir : vec3f 39 | }; -------------------------------------------------------------------------------- /src/framework/math/frustum_cull.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // from: https://gist.github.com/podgorskiy/e698d18879588ada9014768e3e82a644 4 | class Frustum 5 | { 6 | public: 7 | Frustum() {} 8 | 9 | // m = ProjectionMatrix * ViewMatrix 10 | Frustum(const glm::mat4& view_projection); 11 | 12 | void set_view_projection(const glm::mat4& view_projection); 13 | 14 | // http://iquilezles.org/www/articles/frustumcorrect/frustumcorrect.htm 15 | bool is_box_visible(const glm::vec3& minp, const glm::vec3& maxp) const; 16 | 17 | private: 18 | enum Planes 19 | { 20 | Left = 0, 21 | Right, 22 | Bottom, 23 | Top, 24 | Near, 25 | Far, 26 | Count, 27 | Combinations = Count * (Count - 1) / 2 28 | }; 29 | 30 | template 31 | struct ij2k 32 | { 33 | enum { k = i * (9 - i) / 2 + j - 1 }; 34 | }; 35 | 36 | template 37 | glm::vec3 intersection(const glm::vec3* crosses) const; 38 | 39 | glm::vec4 m_planes[Count] = {}; 40 | glm::vec3 m_points[8] = {}; 41 | }; 42 | -------------------------------------------------------------------------------- /src/graphics/primitives/torus_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/primitives/primitive_mesh.h" 4 | 5 | class TorusMesh : public PrimitiveMesh 6 | { 7 | float ring_radius = 1.f; 8 | float tube_radius = 0.2f; 9 | 10 | uint32_t rings = 64u; 11 | uint32_t ring_segments = 32u; 12 | 13 | void build_mesh() override; 14 | 15 | public: 16 | 17 | TorusMesh(float ring_radius = 1.f, float tube_radius = 0.2f, uint32_t rings = 64u, uint32_t ring_segments = 32u, const glm::vec3& color = { 1.f, 1.f, 1.f }); 18 | 19 | void render_gui() override; 20 | 21 | float get_ring_radius() const { return ring_radius; } 22 | float get_tube_radius() const { return tube_radius; } 23 | uint32_t get_rings() const { return rings; } 24 | uint32_t get_ring_segments() const { return ring_segments; } 25 | 26 | void set_ring_radius(float new_ring_radius); 27 | void set_tube_radius(float new_tube_radius); 28 | void set_rings(uint32_t new_rings); 29 | void set_ring_segments(uint32_t new_ring_segments); 30 | }; 31 | -------------------------------------------------------------------------------- /src/graphics/kernels/prefix_sum_kernel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | #include "glm/glm.hpp" 5 | 6 | #include "graphics/pipeline.h" 7 | 8 | #include 9 | 10 | class PrefixSumKernel { 11 | 12 | struct PipelineData { 13 | Pipeline pipeline; 14 | Uniform data_uniform; 15 | Uniform block_sum_uniform; 16 | WGPUBindGroup bind_group; 17 | glm::uvec2 dispatch_size; 18 | }; 19 | 20 | glm::uvec2 workgroup_size; 21 | uint32_t threads_per_workgroup; 22 | uint32_t items_per_workgroup; 23 | 24 | std::vector pipelines; 25 | Shader* shader; 26 | 27 | void create_pass_recursive(WGPUBuffer data, uint32_t data_byte_size, uint32_t count); 28 | 29 | public: 30 | 31 | PrefixSumKernel(WGPUBuffer data, uint32_t data_byte_size, uint32_t count, glm::uvec2 workgroup_size = glm::uvec2(16, 16)); 32 | ~PrefixSumKernel(); 33 | 34 | std::vector get_dispatch_chain(); 35 | 36 | void dispatch(WGPUComputePassEncoder compute_pass); 37 | }; 38 | -------------------------------------------------------------------------------- /src/framework/resources/resource.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Resource { 7 | 8 | public: 9 | 10 | Resource(); 11 | virtual ~Resource() {} 12 | 13 | void ref(); 14 | 15 | // Returns true if last ref 16 | bool unref(); 17 | 18 | virtual void on_delete(); 19 | 20 | void set_name(const std::string& name); 21 | const std::string& get_name() const; 22 | 23 | bool operator==(const Resource& other) const 24 | { 25 | return scene_unique_id == other.scene_unique_id; 26 | } 27 | 28 | void* get_property(const std::string& name); 29 | 30 | std::string get_scene_unique_id() const { return scene_unique_id; } 31 | 32 | uint32_t get_ref_count() { 33 | return ref_count; 34 | } 35 | 36 | private: 37 | 38 | uint32_t ref_count = 0; 39 | std::string scene_unique_id = ""; 40 | 41 | protected: 42 | 43 | std::string name = ""; 44 | 45 | // animatable properties 46 | std::unordered_map properties; 47 | }; 48 | -------------------------------------------------------------------------------- /src/framework/resources/resource.cpp: -------------------------------------------------------------------------------- 1 | #include "resource.h" 2 | 3 | #include "framework/utils/utils.h" 4 | 5 | #include "spdlog/spdlog.h" 6 | 7 | #include 8 | 9 | Resource::Resource() 10 | { 11 | // TODO: set proper unique id for project save/load 12 | // id = reinterpret_cast(this); 13 | 14 | scene_unique_id = generate_unique_id(); 15 | } 16 | 17 | void Resource::ref() 18 | { 19 | ref_count++; 20 | } 21 | 22 | bool Resource::unref() 23 | { 24 | assert(ref_count > 0u); 25 | 26 | ref_count--; 27 | 28 | if (ref_count == 0u) { 29 | on_delete(); 30 | return true; 31 | } 32 | 33 | return false; 34 | } 35 | 36 | void Resource::on_delete() 37 | { 38 | delete this; 39 | } 40 | 41 | void Resource::set_name(const std::string& name) 42 | { 43 | this->name = name; 44 | } 45 | 46 | const std::string& Resource::get_name() const 47 | { 48 | return name; 49 | } 50 | 51 | void* Resource::get_property(const std::string& name) 52 | { 53 | if (properties.contains(name)) { 54 | return properties[name]; 55 | } 56 | 57 | return nullptr; 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 UPF-GTI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/graphics/primitives/quad_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/primitives/primitive_mesh.h" 4 | 5 | class QuadMesh : public PrimitiveMesh 6 | { 7 | float width = 1.0f; 8 | float height = 1.0f; 9 | 10 | uint32_t subdivisions = 0u; 11 | 12 | bool flip_y = false; 13 | bool centered = true; 14 | 15 | void build_mesh() override; 16 | 17 | public: 18 | 19 | QuadMesh(float width = 1.f, float height = 1.f, bool flip_y = false, bool centered = true, uint32_t subdivisions = 0u, const glm::vec3& color = { 1.f, 1.f, 1.f }); 20 | 21 | void render_gui() override; 22 | 23 | float get_width() const { return width; } 24 | float get_height() const { return height; } 25 | uint32_t get_subdivisions() const { return subdivisions; } 26 | bool get_flip_y() const { return flip_y; } 27 | bool get_centered() const { return centered; } 28 | 29 | void set_width(float new_width); 30 | void set_height(float new_height); 31 | void set_subdivisions(uint32_t new_subdivisions); 32 | void set_size(const glm::vec2& size); 33 | void set_centered(bool new_centered); 34 | void set_flip_y(bool new_flip_y); 35 | }; 36 | -------------------------------------------------------------------------------- /data/shaders/mesh_includes.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct VertexInput { 3 | @builtin(instance_index) instance_id : u32, 4 | @location(0) position: vec3f, 5 | #unique vertex @location(1) uv: vec2f, 6 | #unique vertex @location(2) normal: vec3f, 7 | #unique vertex @location(3) tangent: vec4f, 8 | #unique vertex @location(4) color: vec3f, 9 | #unique vertex @location(5) weights: vec4f, 10 | #unique vertex @location(6) joints: vec4i 11 | }; 12 | 13 | struct VertexOutput { 14 | @builtin(position) position: vec4f, 15 | @location(0) uv: vec2f, 16 | @location(1) color: vec4f, 17 | @location(2) world_position: vec3f, 18 | @location(3) normal: vec3f, 19 | @location(4) tangent : vec3f, 20 | @location(5) bitangent : vec3f, 21 | }; 22 | 23 | struct RenderMeshData { 24 | model : mat4x4f 25 | }; 26 | 27 | struct InstanceData { 28 | data : array 29 | } 30 | 31 | struct CameraData { 32 | view_projection : mat4x4f, 33 | view : mat4x4f, 34 | projection : mat4x4f, 35 | eye : vec3f, 36 | exposure : f32, 37 | right_controller_position : vec3f, 38 | ibl_intensity : f32, 39 | screen_size : vec2f, 40 | dummy : vec2f, 41 | }; -------------------------------------------------------------------------------- /src/framework/nodes/mesh_instance_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "node_3d.h" 4 | #include "graphics/mesh.h" 5 | 6 | class MeshInstance3D : public Node3D { 7 | 8 | protected: 9 | Mesh* mesh = nullptr; 10 | 11 | public: 12 | 13 | bool is_skinned = false; 14 | 15 | MeshInstance3D(); 16 | ~MeshInstance3D(); 17 | 18 | void update_aabb(); 19 | void set_aabb(const AABB& new_aabb) override; 20 | void set_surface_material_override(Surface* surface, Material* material); 21 | void set_frustum_culling_enabled(bool enabled); 22 | void set_receive_shadows(bool new_receive_shadows); 23 | void set_mesh(Mesh* new_mesh); 24 | 25 | Mesh* get_mesh() const { return mesh; } 26 | bool get_frustum_culling_enabled(); 27 | Material* get_surface_material(int surface_idx); 28 | Material* get_surface_material_override(Surface* surface); 29 | const std::vector& get_surfaces() const; 30 | Surface* get_surface(int surface_idx) const; 31 | uint32_t get_surface_count() const; 32 | 33 | void add_surface(Surface* surface); 34 | 35 | virtual void render() override; 36 | virtual void update(float delta_time) override; 37 | 38 | void render_gui() override; 39 | }; 40 | -------------------------------------------------------------------------------- /src/framework/nodes/viewport_3d.cpp: -------------------------------------------------------------------------------- 1 | #include "viewport_3d.h" 2 | 3 | #include "graphics/renderer.h" 4 | 5 | #include "framework/nodes/node_2d.h" 6 | #include "framework/parsers/parse_scene.h" 7 | #include "framework/input.h" 8 | 9 | Viewport3D::Viewport3D(Node2D* root_2d) : Node3D(), root(root_2d) 10 | { 11 | root->disable_2d(); 12 | } 13 | 14 | Viewport3D::~Viewport3D() 15 | { 16 | 17 | } 18 | 19 | void Viewport3D::set_viewport_size(const glm::vec2& new_size) 20 | { 21 | viewport_size = new_size; 22 | } 23 | 24 | void Viewport3D::update(float delta_time) 25 | { 26 | if (!active) { 27 | return; 28 | } 29 | 30 | // Manage 3d transform data 31 | 32 | glm::vec2 pos_2d = root->get_translation(); 33 | 34 | auto webgpu_context = Renderer::instance->get_webgpu_context(); 35 | 36 | float width = static_cast(webgpu_context->render_width); 37 | float height = static_cast(webgpu_context->render_height); 38 | float ar = width / height; 39 | 40 | glm::vec2 screen_size(width, height); 41 | pos_2d /= screen_size; 42 | 43 | root->set_position(pos_2d); 44 | root->scale(1.0f / glm::vec2(width, height * ar)); 45 | 46 | root->set_viewport_model(get_global_model()); 47 | } 48 | -------------------------------------------------------------------------------- /src/framework/math/aabb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/vec3.hpp" 4 | #include "glm/mat4x4.hpp" 5 | #include "glm/gtc/quaternion.hpp" 6 | 7 | #include "framework/math/math_utils.h" 8 | 9 | #include 10 | 11 | struct AABB { 12 | glm::vec3 center = {}; 13 | glm::vec3 half_size = {}; 14 | 15 | bool initialized() const; 16 | 17 | /** 18 | * apply a matrix to a box * note that the resulting box will be axis aligned as well 19 | * therefore the resulting box may be larger than the previous 20 | * 21 | * @param box the box to transform 22 | * @param mat the trnsformation matrix to apply 23 | **/ 24 | 25 | AABB transform(const glm::mat4& mat) const; 26 | 27 | AABB rotate(const glm::quat& rotation) const; 28 | 29 | // Returns the index of the longest axis of the bounding box. 30 | int longest_axis() const { 31 | float x_size = abs(half_size.x); 32 | float y_size = abs(half_size.y); 33 | float z_size = abs(half_size.z); 34 | 35 | if (x_size > y_size) 36 | return x_size > z_size ? 0 : 2; 37 | else 38 | return y_size > z_size ? 1 : 2; 39 | } 40 | }; 41 | 42 | AABB merge_aabbs(const AABB& AABB_0, const AABB& AABB_1); 43 | -------------------------------------------------------------------------------- /src/framework/animation/blend_animation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "animation.h" 4 | #include "pose.h" 5 | 6 | #include "framework/nodes/node.h" 7 | 8 | struct BlendTarget { 9 | Animation* animation = nullptr; 10 | float time = 0.0f; 11 | float duration = 0.0f; 12 | float elapsed = 0.0f; 13 | inline BlendTarget() : animation(0), time(0.0f), duration(0.0f), elapsed(0.0f) { } 14 | inline BlendTarget(Animation* target, float duration) : animation(target), time(target->get_start_time()), duration(duration), elapsed(0.0f) { } 15 | }; 16 | 17 | // Fast blend from one animation to another (hide the transition between two animations) 18 | class BlendAnimation { 19 | 20 | protected: 21 | std::vector targets; 22 | Animation* animation = nullptr; 23 | float time = 0.0f; 24 | 25 | public: 26 | BlendAnimation(); 27 | 28 | void play(Animation* target); 29 | void stop(); 30 | void fade_to(Animation* target, float fade_time); 31 | float update(float current_time, uint8_t loop, std::vector& data); 32 | 33 | void blend(Pose& output, Pose& a, Pose& b, float t); 34 | void add(Pose& output, Pose& in, Pose& add_pose, Pose& base); 35 | 36 | Animation* get_current_animation(); 37 | }; 38 | -------------------------------------------------------------------------------- /libraries/glfw3webgpu/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This is only meant to be included as a subdirectory in another project. 2 | # It assumes that targets 'glfw' and 'webgpu' exist. 3 | # Loot at examples/CMakeLists.txt to see how to use it in a project. 4 | 5 | # The glfw3webgpu target 6 | add_library(glfw3webgpu STATIC glfw3webgpu.cpp glfw3webgpu.hpp) 7 | 8 | set_property(TARGET glfw3webgpu PROPERTY LANGUAGES CXX) 9 | set_property(TARGET glfw3webgpu PROPERTY CXX_STANDARD 20) 10 | 11 | target_include_directories(glfw3webgpu PUBLIC .) 12 | target_link_libraries(glfw3webgpu PUBLIC glfw webgpu) 13 | 14 | if (APPLE) 15 | target_compile_options(glfw3webgpu PRIVATE -x objective-c++) 16 | target_link_libraries(glfw3webgpu PRIVATE "-framework Cocoa" "-framework CoreVideo" "-framework IOKit" "-framework QuartzCore") 17 | endif () 18 | 19 | # Copy compile definitions that are PRIVATE in glfw 20 | if (GLFW_BUILD_COCOA) 21 | target_compile_definitions(glfw3webgpu PRIVATE _GLFW_COCOA) 22 | endif() 23 | if (GLFW_BUILD_WIN32) 24 | target_compile_definitions(glfw3webgpu PRIVATE _GLFW_WIN32) 25 | endif() 26 | if (GLFW_BUILD_X11) 27 | target_compile_definitions(glfw3webgpu PRIVATE _GLFW_X11) 28 | endif() 29 | if (GLFW_BUILD_WAYLAND) 30 | target_compile_definitions(glfw3webgpu PRIVATE _GLFW_WAYLAND) 31 | endif() -------------------------------------------------------------------------------- /src/framework/nodes/spot_light_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "light_3d.h" 4 | 5 | class MeshInstance3D; 6 | class Material; 7 | class Surface; 8 | 9 | class SpotLight3D : public Light3D { 10 | 11 | float inner_cone_angle = 0.0f; 12 | float outer_cone_angle = glm::pi() / 4.0f; 13 | 14 | MeshInstance3D* debug_mesh = nullptr; 15 | Surface* debug_surface = nullptr; 16 | 17 | Material* debug_material = nullptr; 18 | 19 | void create_debug_render_cone(); 20 | 21 | public: 22 | 23 | SpotLight3D(); 24 | ~SpotLight3D(); 25 | 26 | virtual void render() override; 27 | 28 | void clone(Node* new_node, bool copy = true) override; 29 | 30 | void render_gui() override; 31 | 32 | void get_uniform_data(sLightUniformData& data) override; 33 | 34 | float get_inner_cone_angle() const { return inner_cone_angle; }; 35 | float get_outer_cone_angle() const { return outer_cone_angle; }; 36 | 37 | void set_range(float value) override; 38 | void on_set_range() override; 39 | 40 | void set_inner_cone_angle(float value); 41 | void set_outer_cone_angle(float value); 42 | 43 | void serialize(std::ofstream& binary_scene_file) override; 44 | void parse(std::ifstream& binary_scene_file) override; 45 | 46 | void create_debug_meshes() override; 47 | }; 48 | -------------------------------------------------------------------------------- /data/shaders/AABB_shader.wgsl: -------------------------------------------------------------------------------- 1 | #include mesh_includes.wgsl 2 | #define GAMMA_CORRECTION 3 | 4 | @group(0) @binding(0) var mesh_data : InstanceData; 5 | 6 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 7 | 8 | @group(2) @binding(1) var albedo: vec4f; 9 | 10 | @vertex 11 | fn vs_main(in: VertexInput) -> VertexOutput { 12 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 13 | 14 | var out: VertexOutput; 15 | var world_position = instance_data.model * vec4f(in.position, 1.0); 16 | out.world_position = world_position.xyz; 17 | out.position = camera_data.view_projection * world_position; 18 | out.uv = in.uv; // forward to the fragment shader 19 | out.color = vec4(in.color, 1.0); 20 | out.normal = in.normal; 21 | return out; 22 | } 23 | 24 | struct FragmentOutput { 25 | @location(0) color: vec4f 26 | } 27 | 28 | @fragment 29 | fn fs_main(in: VertexOutput) -> FragmentOutput { 30 | var out: FragmentOutput; 31 | 32 | var dummy = camera_data.eye; 33 | 34 | var final_color = in.color.rgb * albedo.xyz; 35 | 36 | if ( in.uv.x < 0.015 || in.uv.y > 0.985 || in.uv.x > 0.985 || in.uv.y < 0.015 ) { 37 | out.color = vec4(final_color, 1.0); 38 | } else { 39 | discard; 40 | } 41 | 42 | return out; 43 | } -------------------------------------------------------------------------------- /src/graphics/uniforms_structs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/vec2.hpp" 4 | #include "glm/vec3.hpp" 5 | #include "glm/vec4.hpp" 6 | #include "glm/mat4x4.hpp" 7 | 8 | struct sLightUniformData { 9 | glm::mat4x4 view_proj; 10 | glm::vec3 position; 11 | int type = 0; 12 | glm::vec3 color = { 0.0f, 0.0f, 0.0f }; 13 | float intensity = 0.0f; 14 | glm::vec3 direction = { 0.0f, 0.0f, 0.0f }; 15 | float range = 0.0f; 16 | float shadow_bias = 0.001f; 17 | int cast_shadows = 0; 18 | float inner_cone_cos = 0.0f; 19 | float outer_cone_cos = 0.0f; 20 | }; 21 | 22 | enum sUIDataFlags { 23 | UI_DATA_HOVERED = 1 << 0, 24 | UI_DATA_PRESSED = 1 << 1, 25 | UI_DATA_SELECTED = 1 << 2, 26 | UI_DATA_COLOR_BUTTON = 1 << 3, 27 | UI_DATA_DISABLED = 1 << 4 28 | }; 29 | 30 | struct sUIData { 31 | uint32_t flags = 0u; 32 | float hover_time = 0.0f; 33 | float aspect_ratio = 1.f; 34 | float data_value = 0.0f; // generic data, slider value, group num items, combo buttons use this prop by now to the index in combo.. 35 | 36 | glm::vec3 data_vec = glm::vec3(1.f); // picker color, slider range(+slider_type at .z).. 37 | float clip_range = 0.f; // inside/outside the node parent.. -1..1 38 | 39 | glm::vec2 xr_size = { 0.0f, 0.0f }; 40 | glm::vec2 xr_position = { 0.0f, 0.0f }; 41 | }; 42 | -------------------------------------------------------------------------------- /src/graphics/font.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | #include "framework/utils/json_utils.h" 5 | #include "framework/resources/resource.h" 6 | 7 | #include "font_common.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "glm/vec2.hpp" 14 | #include "glm/vec4.hpp" 15 | 16 | class Texture; 17 | 18 | class Font : public Resource 19 | { 20 | 21 | public: 22 | 23 | ~Font(); 24 | 25 | std::vector textures; 26 | std::map characters; 27 | std::multimap kernings; 28 | 29 | static std::map s_fonts; 30 | 31 | // Info 32 | std::string face; 33 | int size; 34 | int bold; 35 | int italic; 36 | std::vector charset; 37 | int unicode; 38 | int stretchH; 39 | int smooth; 40 | int aa; 41 | glm::vec4 padding; 42 | glm::vec2 spacing; 43 | 44 | // Common 45 | int lineHeight; 46 | int base; 47 | int scaleW; 48 | int scaleH; 49 | int pages; 50 | int packed; 51 | int alphaChnl; 52 | int redChnl; 53 | int greenChnl; 54 | int blueChnl; 55 | int df_range; 56 | 57 | static Font* get(const std::string& font_name); 58 | void load(const std::string& font_name); 59 | float adjust_kerning_pairs(int first, int second); 60 | }; 61 | -------------------------------------------------------------------------------- /src/framework/ui/ui_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace ui { 6 | 7 | const float PICKER_SIZE = 154.f; 8 | const float BUTTON_SIZE = 64.f; 9 | const float GROUP_MARGIN = 12.f; 10 | const float TEXT_SHADOW_MARGIN = 1.f; 11 | 12 | const float HOVER_HAPTIC_AMPLITUDE = 0.05f; 13 | const float HOVER_HAPTIC_DURATION = 0.1f; 14 | 15 | enum ComboIndex { 16 | UNIQUE, 17 | FIRST, 18 | MIDDLE, 19 | LAST 20 | }; 21 | 22 | enum Node2DFlags : uint32_t { 23 | SELECTED = 1 << 0, 24 | DISABLED = 1 << 1, 25 | UNIQUE_SELECTION = 1 << 2, 26 | ALLOW_TOGGLE = 1 << 3, 27 | SKIP_NAME = 1 << 4, 28 | SKIP_VALUE = 1 << 5, 29 | USER_RANGE = 1 << 6, 30 | CURVE_INV_POW = 1 << 7, 31 | TEXT_CENTERED = 1 << 8, 32 | TEXT_EVENTS = 1 << 9, 33 | TEXT_SELECTABLE = 1 << 10, 34 | SKIP_TEXT_RECT = 1 << 11, 35 | SCROLLABLE = 1 << 12, 36 | DBL_CLICK = 1 << 13, 37 | LONG_CLICK = 1 << 14, 38 | SKIP_HOVER_SCALE = 1 << 15, 39 | HIDDEN = 1 << 16, 40 | CONFIRM_BUTTON = 1 << 17, 41 | CREATE_3D = 1 << 18, 42 | KEEP_SUBMENU_OPENED = 1 << 19, 43 | CURVED_PANEL = 1 << 20, 44 | FULLSCREEN = 1 << 21, 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /data/shaders/mesh_transparent.wgsl: -------------------------------------------------------------------------------- 1 | #include mesh_includes.wgsl 2 | 3 | #define GAMMA_CORRECTION 4 | 5 | @group(0) @binding(0) var mesh_data : InstanceData; 6 | 7 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 8 | 9 | @group(2) @binding(1) var albedo: vec4f; 10 | 11 | @vertex 12 | fn vs_main(in: VertexInput) -> VertexOutput { 13 | 14 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 15 | 16 | var out: VertexOutput; 17 | out.uv = in.uv; // forward to the fragment shader 18 | out.color = vec4(in.color, 1.0) * albedo; 19 | out.normal = (instance_data.model * vec4f(in.normal, 0.0)).xyz; 20 | 21 | var world_position = instance_data.model * vec4f(in.position, 1.0); 22 | out.world_position = world_position.xyz; 23 | out.position = camera_data.view_projection * world_position; 24 | 25 | return out; 26 | } 27 | 28 | struct FragmentOutput { 29 | @location(0) color: vec4f 30 | } 31 | 32 | @fragment 33 | fn fs_main(in: VertexOutput) -> FragmentOutput { 34 | 35 | var out: FragmentOutput; 36 | var dummy = camera_data.eye; 37 | 38 | var final_color : vec3f = in.color.rgb; 39 | 40 | if (GAMMA_CORRECTION == 1) { 41 | final_color = pow(final_color, vec3f(1.0 / 2.2)); 42 | } 43 | 44 | out.color = vec4f(final_color, 0.3); 45 | 46 | return out; 47 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libraries/openxr"] 2 | path = libraries/openxr 3 | url = https://github.com/KhronosGroup/OpenXR-SDK 4 | [submodule "libraries/webgpu"] 5 | path = libraries/webgpu 6 | url = https://github.com/upf-gti/WebGPU-distribution 7 | [submodule "libraries/glm"] 8 | path = libraries/glm 9 | url = https://github.com/g-truc/glm.git 10 | [submodule "libraries/spdlog"] 11 | path = libraries/spdlog 12 | url = https://github.com/gabime/spdlog.git 13 | [submodule "libraries/imgui"] 14 | path = libraries/imgui 15 | url = https://github.com/ocornut/imgui 16 | [submodule "libraries/glfw"] 17 | path = libraries/glfw 18 | url = https://github.com/glfw/glfw.git 19 | [submodule "src/xr/dawnxr"] 20 | path = src/xr/dawnxr 21 | url = https://github.com/upf-gti/dawnxr 22 | [submodule "libraries/easyVDB"] 23 | path = libraries/easyVDB 24 | url = https://github.com/victorubieto/easyVDB 25 | [submodule "libraries/Vulkan-Headers"] 26 | path = libraries/Vulkan-Headers 27 | url = https://github.com/KhronosGroup/Vulkan-Headers.git 28 | [submodule "libraries/mikktspace"] 29 | path = libraries/mikktspace 30 | url = https://github.com/mmikk/MikkTSpace.git 31 | [submodule "libraries/rapidjson"] 32 | path = libraries/rapidjson 33 | url = https://github.com/Tencent/rapidjson 34 | [submodule "libraries/emscripten-webxr"] 35 | path = libraries/emscripten-webxr 36 | url = https://github.com/upf-gti/emscripten-webxr.git 37 | -------------------------------------------------------------------------------- /src/framework/nodes/text_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "mesh_instance_3d.h" 5 | #include "framework/colors.h" 6 | #include "graphics/font_common.h" 7 | 8 | class Font; 9 | 10 | class Text3D : public MeshInstance3D { 11 | 12 | sSurfaceData vertices; 13 | 14 | Font* font = nullptr; 15 | float font_scale = 1.f; 16 | 17 | std::string text = ""; 18 | Color color; 19 | 20 | glm::vec2 box_size; 21 | bool wrap = true; 22 | bool is_2d = false; 23 | 24 | void append_char(const glm::vec3& pos, const Character& c); 25 | 26 | public: 27 | 28 | Text3D(const std::string& text, const Color& color = colors::WHITE, bool is_2d = false, const glm::vec2& box_size = { 1, 1 }, bool wrap = false); 29 | virtual ~Text3D(); 30 | 31 | virtual void update(float delta_time) override; 32 | 33 | void generate_mesh(); 34 | 35 | int get_text_width(const std::string& text); 36 | int get_text_height(const std::string& text); 37 | const std::string& get_text() const { return text; } 38 | float get_scale() const { return font_scale; } 39 | bool get_wrap() const { return wrap; } 40 | const glm::vec2& get_box_size() const { return box_size; } 41 | 42 | void set_text(const std::string& p_text); 43 | void set_scale(float new_scale); 44 | void set_wrap(bool new_wrap); 45 | void set_is_2d(bool new_is_2d); 46 | void set_box_size(const glm::vec2& new_box_size); 47 | 48 | void render_gui() override; 49 | }; 50 | -------------------------------------------------------------------------------- /src/framework/utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | 5 | #include 6 | #include 7 | 8 | enum Error { 9 | OK, 10 | FAILED, 11 | ERR_NOT_FOUND, 12 | ERR_CANT_CREATE, 13 | ERR_MAX, 14 | }; 15 | 16 | #define assert_msg(condition, msg) if (!(condition)) { spdlog::error(msg); assert(false);} 17 | #define _STR(m_x) #m_x 18 | 19 | std::string remove_special_characters(const std::string& str); 20 | std::vector tokenize(const std::string & str, char token = ' '); 21 | 22 | void print_line(const char* line); 23 | void print_line(const std::string & line); 24 | void print_error(const char* p_function, const char* p_file, int p_line, const char* p_error, const char* p_message); 25 | 26 | void to_camel_case(std::string& str); 27 | 28 | bool read_file(const std::string & filename, std::string & content); 29 | 30 | #define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ 31 | if (m_cond) { \ 32 | print_error(__FUNCTION__, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \ 33 | return m_retval; \ 34 | } 35 | 36 | // https://stackoverflow.com/a/8518855 37 | std::string dirname_of_file(const std::string& fname); 38 | 39 | std::string generate_unique_id(uint32_t num_characters = 6u); 40 | -------------------------------------------------------------------------------- /src/graphics/primitives/cylinder_mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/primitives/primitive_mesh.h" 4 | 5 | class CylinderMesh : public PrimitiveMesh 6 | { 7 | float top_radius = 0.5f; 8 | float bottom_radius = 0.5f; 9 | float height = 2.0f; 10 | 11 | uint32_t rings = 4u; 12 | uint32_t ring_segments = 64u; 13 | 14 | bool cap_top = true; 15 | bool cap_bottom = true; 16 | 17 | void build_mesh() override; 18 | 19 | public: 20 | 21 | CylinderMesh(float top_radius = 0.5f, float bottom_radius = 0.5f, float height = 2.0f, uint32_t rings = 4u, uint32_t ring_segments = 64u, bool cap_top = true, bool cap_bottom = true, const glm::vec3& color = { 1.f, 1.f, 1.f }); 22 | 23 | void render_gui() override; 24 | 25 | float get_top_radius() const { return top_radius; } 26 | float get_bottom_radius() const { return bottom_radius; } 27 | float get_height() const { return height; } 28 | uint32_t get_rings() const { return rings; } 29 | uint32_t get_ring_segments() const { return ring_segments; } 30 | bool get_cap_top() const { return cap_top; } 31 | bool get_cap_bottom() const { return cap_bottom; } 32 | 33 | void set_top_radius(float new_top_radius); 34 | void set_bottom_radius(float new_bottom_radius); 35 | void set_height(float new_height); 36 | void set_rings(uint32_t new_rings); 37 | void set_ring_segments(uint32_t new_ring_segments); 38 | void set_cap_top(bool new_cap_top); 39 | void set_cap_bottom(bool new_cap_bottom); 40 | }; 41 | -------------------------------------------------------------------------------- /src/framework/camera/flyover_camera.cpp: -------------------------------------------------------------------------------- 1 | #include "flyover_camera.h" 2 | 3 | #include "framework/input.h" 4 | 5 | #include "glm/gtx/norm.hpp" 6 | 7 | FlyoverCamera::FlyoverCamera() : Camera3D() 8 | { 9 | } 10 | 11 | void FlyoverCamera::update(float delta_time) 12 | { 13 | Camera3D::update(delta_time); 14 | 15 | float final_speed = speed; 16 | glm::vec3 move_dir = glm::vec3(0.0f, 0.0f, 0.0f); 17 | if (Input::is_key_pressed(GLFW_KEY_LEFT_SHIFT)) final_speed *= 10.0f; 18 | if (Input::is_key_pressed(GLFW_KEY_LEFT_CONTROL)) final_speed *= 0.1f; 19 | if (Input::is_key_pressed(GLFW_KEY_W) || Input::is_key_pressed(GLFW_KEY_UP)) move_dir += (glm::vec3(0.0f, 0.0f, -1.0f)); 20 | if (Input::is_key_pressed(GLFW_KEY_S) || Input::is_key_pressed(GLFW_KEY_DOWN)) move_dir += (glm::vec3(0.0f, 0.0f, 1.0f)); 21 | if (Input::is_key_pressed(GLFW_KEY_A) || Input::is_key_pressed(GLFW_KEY_LEFT)) move_dir += (glm::vec3(-1.0f, 0.0f, 0.0f)); 22 | if (Input::is_key_pressed(GLFW_KEY_D) || Input::is_key_pressed(GLFW_KEY_RIGHT)) move_dir += (glm::vec3(1.0f, 0.0f, 0.0f)); 23 | 24 | if (glm::length2(move_dir)) { 25 | move_dir = get_local_vector(move_dir); 26 | move_dir = normalize(move_dir) * final_speed; 27 | } 28 | 29 | glm::vec3 new_forward = yaw_pitch_to_vector(delta_yaw_lerp.value, delta_pitch_lerp.value); 30 | eye_lerp.value = smooth_damp(eye_lerp.value, eye + move_dir, &eye_lerp.velocity, 0.4f, 500.0f, delta_time); 31 | 32 | look_at(eye_lerp.value, eye_lerp.value + new_forward, glm::vec3(0.0f, 1.0f, 0.0f), false); 33 | } 34 | -------------------------------------------------------------------------------- /data/shaders/kernels/radix_sort_reorder.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var inputKeys: array; 2 | @group(0) @binding(1) var outputKeys: array; 3 | @group(0) @binding(2) var local_prefix_sum: array; 4 | @group(0) @binding(3) var prefix_block_sum: array; 5 | @group(0) @binding(4) var inputValues: array; 6 | @group(0) @binding(5) var outputValues: array; 7 | 8 | override WORKGROUP_COUNT: u32; 9 | override THREADS_PER_WORKGROUP: u32; 10 | override WORKGROUP_SIZE_X: u32; 11 | override WORKGROUP_SIZE_Y: u32; 12 | override CURRENT_BIT: u32; 13 | override ELEMENT_COUNT: u32; 14 | 15 | @compute @workgroup_size(WORKGROUP_SIZE_X, WORKGROUP_SIZE_Y, 1) 16 | fn radix_sort_reorder( 17 | @builtin(workgroup_id) w_id: vec3, 18 | @builtin(num_workgroups) w_dim: vec3, 19 | @builtin(local_invocation_index) TID: u32, // Local thread ID 20 | ) { 21 | let WORKGROUP_ID = w_id.x + w_id.y * w_dim.x; 22 | let WID = WORKGROUP_ID * THREADS_PER_WORKGROUP; 23 | let GID = WID + TID; // Global thread ID 24 | 25 | if (GID >= ELEMENT_COUNT) { 26 | return; 27 | } 28 | 29 | let k = inputKeys[GID]; 30 | let v = inputValues[GID]; 31 | 32 | let local_prefix = local_prefix_sum[GID]; 33 | 34 | // Calculate new position 35 | let extract_bits = (k >> CURRENT_BIT) & 0x3; 36 | let pid = extract_bits * WORKGROUP_COUNT + WORKGROUP_ID; 37 | let sorted_position = prefix_block_sum[pid] + local_prefix; 38 | 39 | outputKeys[sorted_position] = k; 40 | outputValues[sorted_position] = v; 41 | } -------------------------------------------------------------------------------- /src/framework/nodes/skeleton_instance_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/nodes/node_3d.h" 4 | 5 | struct Uniform; 6 | class Skeleton; 7 | class SkeletonHelper3D; 8 | class Joint3D; 9 | 10 | class SkeletonInstance3D : public Node3D { 11 | 12 | Uniform* animated_uniform_data = nullptr; 13 | Uniform* invbind_uniform_data = nullptr; 14 | 15 | protected: 16 | 17 | std::vector joint_nodes; 18 | 19 | Skeleton* skeleton = nullptr; 20 | 21 | SkeletonHelper3D* helper = nullptr; 22 | 23 | void recursive_tree_gui(Node* node); 24 | 25 | public: 26 | 27 | SkeletonInstance3D(); 28 | ~SkeletonInstance3D(); 29 | 30 | virtual void initialize() override; 31 | 32 | void update(float delta_time) override; 33 | void render() override; 34 | void render_gui() override; 35 | 36 | void update_pose_from_joints(); 37 | virtual void update_joints_from_pose(); 38 | 39 | Skeleton* get_skeleton(); 40 | Node* get_node(std::vector& path_tokens) override; 41 | std::vector get_animated_data(); 42 | std::vector get_invbind_data(); 43 | Uniform* get_animated_uniform_data() { return animated_uniform_data; } 44 | Uniform* get_invbind_uniform_data() { return invbind_uniform_data; } 45 | 46 | void set_skeleton(Skeleton* new_skeleton, const std::vector& new_joint_nodes = {}); 47 | void set_uniform_data(Uniform* animated_u, Uniform* invbind_u); 48 | 49 | void generate_joints_from_pose(); 50 | 51 | bool test_ray_collision(const glm::vec3& ray_origin, const glm::vec3& ray_direction, float* distance, Node3D** out = nullptr) override; 52 | }; 53 | 54 | -------------------------------------------------------------------------------- /src/framework/animation/pose.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "framework/math/transform.h" 3 | #include 4 | 5 | // Used to hold the transformation of every bone in an animated hierarchy 6 | class Pose 7 | { 8 | protected: 9 | std::vector joints; // local transforms 10 | std::vector parents; // parent joints Id (index in the joints array) 11 | public: 12 | 13 | Pose(); // Empty constructor 14 | // Initialize the pose given another pose 15 | Pose(const Pose& p); 16 | // Initialize the pose given the number of joints of the pose 17 | Pose(size_t numJoints); 18 | 19 | // Resize the array of joints and of parents id 20 | void resize(size_t size); 21 | size_t size() const; 22 | 23 | void set_parent(size_t id, int parent_id); 24 | void set_parents(const std::vector& new_parents) { parents = new_parents; }; 25 | void set_joints(const std::vector& new_joints) { joints = new_joints; }; 26 | 27 | int get_parent(size_t id); 28 | const std::vector& get_joints() const { return joints; }; 29 | const std::vector& get_parents() const { return parents; }; 30 | 31 | // Set the transformation for the joint given its id 32 | void set_local_transform(size_t id, const Transform& transform); 33 | // Get the transformation of the joint given its id 34 | Transform& get_local_transform(size_t id); 35 | // Get the global transformation (world space) of the joint 36 | Transform get_global_transform(size_t id); 37 | // Get the global transformation matrix (world space) of all the joints 38 | std::vector get_global_matrices(); 39 | Transform operator[](size_t index); 40 | Pose& operator=(const Pose& p); 41 | }; 42 | -------------------------------------------------------------------------------- /data/shaders/mesh_shadow.wgsl: -------------------------------------------------------------------------------- 1 | #include mesh_includes.wgsl 2 | 3 | struct ShadowVertexInput { 4 | @builtin(instance_index) instance_id : u32, 5 | @location(0) position: vec3f, 6 | #ifdef USE_SKINNING 7 | #unique vertex @location(5) weights: vec4f, 8 | #unique vertex @location(6) joints: vec4i 9 | #endif 10 | }; 11 | 12 | struct ShadowVertexOutput { 13 | @location(0) shadowPos: vec3f, 14 | @location(1) fragPos: vec3f, 15 | @location(2) fragNorm: vec3f, 16 | 17 | @builtin(position) Position: vec4f, 18 | } 19 | 20 | @group(0) @binding(0) var mesh_data : InstanceData; 21 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 22 | 23 | #ifdef USE_SKINNING 24 | @group(2) @binding(10) var animated_matrices: array; 25 | @group(2) @binding(11) var inv_bind_matrices: array; 26 | #endif 27 | 28 | @vertex 29 | fn vs_main(in : ShadowVertexInput) -> @builtin(position) vec4f { 30 | 31 | var position = vec4f(in.position, 1.0); 32 | 33 | #ifdef USE_SKINNING 34 | var skin : mat4x4f = (animated_matrices[in.joints.x] * inv_bind_matrices[in.joints.x]) * in.weights.x; 35 | skin += (animated_matrices[in.joints.y] * inv_bind_matrices[in.joints.y]) * in.weights.y; 36 | skin += (animated_matrices[in.joints.z] * inv_bind_matrices[in.joints.z]) * in.weights.z; 37 | skin += (animated_matrices[in.joints.w] * inv_bind_matrices[in.joints.w]) * in.weights.w; 38 | position = skin * position; 39 | normals = skin * normals; 40 | #endif 41 | 42 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 43 | 44 | return camera_data.view_projection * instance_data.model * position; 45 | } 46 | -------------------------------------------------------------------------------- /src/graphics/uniform.cpp: -------------------------------------------------------------------------------- 1 | #include "uniform.h" 2 | 3 | Uniform::Uniform() 4 | { 5 | } 6 | 7 | Uniform::~Uniform() 8 | { 9 | //destroy(); 10 | } 11 | 12 | void Uniform::destroy() 13 | { 14 | if (std::holds_alternative(data)) { 15 | wgpuBufferDestroy(std::get(data)); 16 | } 17 | else if (std::holds_alternative(data)) { 18 | wgpuTextureViewRelease(std::get(data)); 19 | } 20 | else if (std::holds_alternative(data)) { 21 | wgpuSamplerRelease(std::get(data)); 22 | } 23 | 24 | data = {}; 25 | } 26 | 27 | WGPUBindGroupEntry Uniform::get_bind_group_entry() const 28 | { 29 | // Create a binding 30 | WGPUBindGroupEntry bindingGroup = {}; 31 | bindingGroup.nextInChain = nullptr; 32 | 33 | // The index of the binding (the entries in bindGroupDesc can be in any order) 34 | bindingGroup.binding = binding; 35 | 36 | if (std::holds_alternative(data)) { 37 | // The buffer it is actually bound to 38 | bindingGroup.buffer = std::get(data); 39 | // We can specify an offset within the buffer, so that a single buffer can hold 40 | // multiple uniform blocks. 41 | bindingGroup.offset = 0; 42 | // And we specify again the size of the buffer. 43 | bindingGroup.size = buffer_size; 44 | } 45 | else if (std::holds_alternative(data)) { 46 | bindingGroup.textureView = std::get(data); 47 | } 48 | else if (std::holds_alternative(data)) { 49 | bindingGroup.sampler = std::get(data); 50 | } 51 | 52 | return bindingGroup; 53 | } 54 | -------------------------------------------------------------------------------- /src/framework/animation/skeleton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pose.h" 4 | #include "framework/resources/resource.h" 5 | 6 | class Skeleton : public Resource 7 | { 8 | Pose bind_pose; 9 | Pose rest_pose; 10 | Pose current_pose; 11 | 12 | std::vector inv_bind_pose; // vector of inverse bind pose matrix of each joint 13 | std::vector joint_names; // vector of the name of each joint 14 | std::vector joint_ids; // vector of the id of each joint 15 | 16 | // updates the inverse bind pose matrices: any time the bind pose of the skeleton is updated, the inverse bind pose should be re-calculated as well 17 | void update_inv_bind_pose(); 18 | 19 | public: 20 | 21 | Skeleton(); // Empty constructor 22 | 23 | // Initialize the skeleton given the rest and bind poses, and the names of the joints 24 | Skeleton(const Pose& rest, const Pose& bind, const std::vector& names, const std::vector& ids = {}); 25 | 26 | virtual ~Skeleton(); 27 | 28 | void set(const Pose& rest, const Pose& bind, const std::vector& names, const std::vector& ids = {}); 29 | 30 | Pose& get_bind_pose(); 31 | Pose& get_rest_pose(); 32 | Pose& get_current_pose(); 33 | 34 | const std::vector& get_inv_bind_pose(); 35 | const std::vector& get_joint_names(); 36 | const std::vector& get_joint_indices(); 37 | std::string& get_joint_name(uint32_t id); 38 | uint32_t get_joint_index(uint32_t id); 39 | uint32_t get_joints_count() const; 40 | 41 | void set_current_pose(const Pose& pose); 42 | 43 | void serialize(std::ofstream& binary_scene_file); 44 | void parse(std::ifstream& binary_scene_file); 45 | }; 46 | -------------------------------------------------------------------------------- /src/graphics/mesh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "material.h" 4 | #include "surface.h" 5 | #include "framework/animation/skeleton.h" 6 | #include 7 | 8 | class Shader; 9 | class Texture; 10 | class Skeleton; 11 | class Node; 12 | 13 | class Mesh { 14 | 15 | protected: 16 | 17 | std::string mesh_type; 18 | 19 | Node* node_ref = nullptr; 20 | Skeleton* skeleton = nullptr; 21 | 22 | std::vector surfaces; 23 | std::unordered_map material_overrides; 24 | 25 | bool frustum_culling_enabled = true; 26 | bool receive_shadows = false; 27 | 28 | public: 29 | 30 | Mesh(); 31 | virtual ~Mesh(); 32 | 33 | Material* get_surface_material(int surface_idx); 34 | bool get_frustum_culling_enabled() const; 35 | bool get_receive_shadows() const { return receive_shadows; } 36 | Material* get_surface_material_override(Surface* surface); 37 | const std::vector& get_surfaces() const; 38 | std::vector& get_surfaces(); 39 | Surface* get_surface(int surface_idx) const; 40 | uint32_t get_surface_count() const; 41 | Skeleton* get_skeleton() const; 42 | Node* get_node_ref() const { return node_ref; } 43 | const std::string& get_mesh_type() const { return mesh_type; } 44 | 45 | void set_surface_material_override(Surface* surface, Material* material); 46 | void set_frustum_culling_enabled(bool enabled); 47 | void set_receive_shadows(bool receive_shadows) { this->receive_shadows = receive_shadows; } 48 | void set_node_ref(Node* node) { node_ref = node; } 49 | void set_mesh_type(const std::string& new_mesh_type) { mesh_type = new_mesh_type; } 50 | void set_skeleton(Skeleton* s); 51 | 52 | void add_surface(Surface* surface); 53 | 54 | virtual void render_gui(); 55 | }; 56 | -------------------------------------------------------------------------------- /src/framework/parsers/parse_gltf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "parser.h" 4 | 5 | #include "framework/utils/hash.h" 6 | 7 | class Node3D; 8 | class Texture; 9 | class Surface; 10 | 11 | struct GltfPrimitive { 12 | std::map attributes; 13 | int material = -1; 14 | int indices = -1; 15 | int mode = -1; 16 | }; 17 | 18 | template <> 19 | struct std::hash 20 | { 21 | std::size_t operator()(const GltfPrimitive& k) const 22 | { 23 | using std::size_t; 24 | using std::hash; 25 | using std::string; 26 | 27 | std::vector hs; 28 | 29 | hs.push_back(hash()(k.material)); 30 | hs.push_back(hash()(k.indices)); 31 | hs.push_back(hash()(k.mode)); 32 | 33 | for (auto& attrib : k.attributes) { 34 | hs.push_back(hash()(attrib.second)); 35 | } 36 | 37 | std::size_t seed = 0; 38 | hash_combine(seed, hs); 39 | return seed; 40 | } 41 | }; 42 | 43 | namespace tinygltf { 44 | class Model; 45 | } 46 | 47 | class GltfParser : public Parser { 48 | 49 | Node3D* root = nullptr; 50 | 51 | std::map texture_cache; 52 | std::map mesh_cache; 53 | 54 | bool parse_model(tinygltf::Model* model, std::vector& entities, uint32_t flags = PARSE_DEFAULT); 55 | 56 | void on_async_finished() override; 57 | 58 | public: 59 | 60 | bool parse(std::string file_path, std::vector& entities, uint32_t flags = PARSE_DEFAULT) override; 61 | 62 | bool read_data(int8_t* byte_array, uint32_t array_size, Node* scene_root, std::vector& entities, uint32_t flags = PARSE_DEFAULT) override; 63 | 64 | void push_scene_root(Node3D* new_root) { root = new_root; }; 65 | 66 | void clear_cache(); 67 | }; 68 | -------------------------------------------------------------------------------- /src/framework/nodes/look_at_ik_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/nodes/node_3d.h" 4 | #include "framework/animation/skeleton.h" 5 | 6 | #include 7 | #include 8 | 9 | class SkeletonInstance3D; 10 | class IKSolver; 11 | class FABRIKSolver; 12 | 13 | class LookAtIK3D : public Node 14 | { 15 | uint32_t max_iterations = 15u; 16 | float min_distance = 0.1f; 17 | 18 | std::string root_name = ""; 19 | std::string end_effector = ""; 20 | 21 | Transform target; 22 | 23 | enum IKSolvers { FABRIK, CCD, JACOBIAN }; 24 | uint32_t solver = IKSolvers::FABRIK; 25 | IKSolver* ik_solver = nullptr; 26 | 27 | SkeletonInstance3D* skeleton_instance = nullptr; 28 | 29 | public: 30 | 31 | LookAtIK3D(); 32 | LookAtIK3D(SkeletonInstance3D* new_skeleton_instance); 33 | 34 | ~LookAtIK3D() {}; 35 | 36 | void set_iterations(uint32_t iterations); 37 | void set_distance(float distance); 38 | void set_root_name(const std::string& new_root_name) { root_name = new_root_name; } 39 | void set_end_effector(const std::string& new_end_effector) { end_effector = new_end_effector; } 40 | void set_solver(uint32_t solver_type); 41 | void set_target(Transform new_target) { target = new_target; }; 42 | void set_skeleton_instance(SkeletonInstance3D* new_skeleton_instance) { skeleton_instance = new_skeleton_instance; }; 43 | 44 | uint32_t get_iterations() { return max_iterations; } 45 | float get_distance() { return min_distance; } 46 | const std::string& get_root_name() { return root_name; } 47 | const std::string& get_end_effector() { return end_effector; }; 48 | const Transform& get_target() { return target; } 49 | uint32_t get_solver() { return solver; } 50 | 51 | void update(float delta_time); 52 | void render_gui(); 53 | 54 | void create_chain(); 55 | }; 56 | -------------------------------------------------------------------------------- /data/shaders/ui/ui_panel.wgsl: -------------------------------------------------------------------------------- 1 | #include ui_includes.wgsl 2 | #include ../mesh_includes.wgsl 3 | 4 | #define GAMMA_CORRECTION 5 | 6 | @group(0) @binding(0) var mesh_data : InstanceData; 7 | 8 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 9 | 10 | #ifdef ALBEDO_TEXTURE 11 | @group(2) @binding(0) var albedo_texture: texture_2d; 12 | #endif 13 | 14 | @group(2) @binding(1) var albedo: vec4f; 15 | 16 | #ifdef USE_SAMPLER 17 | @group(2) @binding(7) var texture_sampler : sampler; 18 | #endif 19 | 20 | @group(3) @binding(0) var ui_data : UIData; 21 | 22 | @vertex 23 | fn vs_main(in: VertexInput) -> VertexOutput { 24 | 25 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 26 | var out: VertexOutput; 27 | 28 | var world_position : vec4f = instance_data.model * vec4f(in.position, 1.0); 29 | out.world_position = world_position.xyz; 30 | out.position = camera_data.view_projection * world_position; 31 | out.uv = in.uv; // forward to the fragment shader 32 | out.color = vec4(in.color, 1.0) * albedo; 33 | out.normal = in.normal; 34 | return out; 35 | } 36 | 37 | struct FragmentOutput { 38 | @location(0) color: vec4f 39 | } 40 | 41 | @fragment 42 | fn fs_main(in: VertexOutput) -> FragmentOutput { 43 | 44 | var dummy = camera_data.eye; 45 | let dummy1 : vec2f = ui_data.xr_size; 46 | 47 | var out: FragmentOutput; 48 | 49 | #ifdef ALBEDO_TEXTURE 50 | var color : vec4f = textureSample(albedo_texture, texture_sampler, in.uv); 51 | #else 52 | var color : vec4f = in.color; 53 | #endif 54 | 55 | var final_color : vec3f = color.rgb; 56 | var alpha : f32; 57 | 58 | if (GAMMA_CORRECTION == 1) { 59 | final_color = pow(final_color, vec3f(1.0 / 2.2)); 60 | } 61 | 62 | out.color = vec4f(final_color, color.a); 63 | 64 | return out; 65 | } -------------------------------------------------------------------------------- /data/shaders/ui/ui_texture.wgsl: -------------------------------------------------------------------------------- 1 | #include ui_includes.wgsl 2 | #include ../mesh_includes.wgsl 3 | 4 | #define GAMMA_CORRECTION 5 | 6 | @group(0) @binding(0) var mesh_data : InstanceData; 7 | 8 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 9 | 10 | #ifdef ALBEDO_TEXTURE 11 | @group(2) @binding(0) var albedo_texture: texture_2d; 12 | #endif 13 | 14 | @group(2) @binding(1) var albedo: vec4f; 15 | 16 | #ifdef USE_SAMPLER 17 | @group(2) @binding(7) var texture_sampler : sampler; 18 | #endif 19 | 20 | @group(3) @binding(0) var ui_data : UIData; 21 | 22 | @vertex 23 | fn vs_main(in: VertexInput) -> VertexOutput { 24 | 25 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 26 | 27 | var out: VertexOutput; 28 | var world_position = instance_data.model * vec4f(in.position, 1.0); 29 | out.world_position = world_position.xyz; 30 | out.position = camera_data.view_projection * world_position; 31 | out.uv = in.uv; 32 | out.color = vec4(in.color, 1.0) * albedo; 33 | out.normal = in.normal; 34 | return out; 35 | } 36 | 37 | struct FragmentOutput { 38 | @location(0) color: vec4f 39 | } 40 | 41 | @fragment 42 | fn fs_main(in: VertexOutput) -> FragmentOutput { 43 | 44 | var dummy = camera_data.eye; 45 | let dummy1 = ui_data.data_value; 46 | 47 | if(ui_data.clip_range < 0.0 && in.uv.y < abs(ui_data.clip_range) ) { 48 | discard; 49 | } 50 | else if(ui_data.clip_range > 0.0 && in.uv.y > ui_data.clip_range ) { 51 | discard; 52 | } 53 | 54 | var out: FragmentOutput; 55 | var color : vec4f = textureSample(albedo_texture, texture_sampler, in.uv); 56 | 57 | var _color = color.rgb; 58 | 59 | if (GAMMA_CORRECTION == 1) { 60 | _color = pow(_color, vec3f(1.0 / 2.2)); 61 | } 62 | 63 | out.color = vec4f(_color, color.a); 64 | 65 | return out; 66 | } -------------------------------------------------------------------------------- /libraries/glfw3webgpu/glfw3webgpu.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an extension of GLFW for WebGPU, abstracting away the details of 3 | * OS-specific operations. 4 | * 5 | * This file is part of the "Learn WebGPU for C++" book. 6 | * https://eliemichel.github.io/LearnWebGPU 7 | * 8 | * MIT License 9 | * Copyright (c) 2022-2023 Elie Michel and the wgpu-native authors 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in all 19 | * copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | * SOFTWARE. 28 | */ 29 | 30 | #ifndef _glfw3_webgpu_h_ 31 | #define _glfw3_webgpu_h_ 32 | 33 | #include 34 | #include 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | 40 | /** 41 | * Get a WGPUSurface from a GLFW window. 42 | */ 43 | WGPUSurface glfwGetWGPUSurface(WGPUInstance instance, GLFWwindow* window); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | 49 | #endif // _glfw3_webgpu_h_ 50 | -------------------------------------------------------------------------------- /src/graphics/hdre.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define N_LEVELS 6 7 | #define N_FACES 6 8 | 9 | struct sHDREHeader { 10 | 11 | char signature[4]; 12 | float version; 13 | 14 | short width; 15 | short height; 16 | 17 | float maxFileSize; 18 | 19 | short numChannels; 20 | short bitsPerChannel; 21 | short headerSize; 22 | short endianEncoding; 23 | 24 | float maxLuminance; 25 | short type; 26 | 27 | short includesSH; 28 | float numCoeffs; 29 | float coeffs[27]; 30 | }; 31 | 32 | struct sHDRELevel { 33 | int width; 34 | int height; 35 | float* data; 36 | float** faces; 37 | }; 38 | 39 | class HDRE { 40 | 41 | private: 42 | 43 | float* data = nullptr; // only f32 now 44 | float* pixels[N_LEVELS][N_FACES] = {}; // Xpos, Xneg, Ypos, Yneg, Zpos, Zneg 45 | float* faces_array[N_LEVELS] = {}; 46 | 47 | sHDREHeader header; 48 | 49 | bool process_header(); 50 | bool clean(); 51 | 52 | public: 53 | 54 | int width = 0; 55 | int height = 0; 56 | 57 | std::string name; 58 | float version = 0.0f; 59 | short numChannels = 0; 60 | short bitsPerChannel = 0; 61 | float maxLuminance = 0; 62 | 63 | short type = 0; 64 | float numCoeffs = 0.0f; 65 | float* coeffs = nullptr; 66 | 67 | HDRE() {}; 68 | ~HDRE(); 69 | 70 | // class manager 71 | static std::map sHDRELoaded; 72 | 73 | bool load(const std::string& filename); 74 | 75 | static HDRE* Get(const std::string& filename); 76 | void setName(const std::string& name); 77 | 78 | // useful methods 79 | float getMaxLuminance() { return this->header.maxLuminance; }; 80 | //float* getSHCoeffs() { if (this->numCoeffs > 0) return this->header.coeffs; return nullptr; } 81 | 82 | float* getData(); // All pixel data 83 | float* getFace(int level, int face); // Specific level and face 84 | float** getFaces(int level = 0); // [[]]: Array per face with all level data 85 | 86 | sHDRELevel getLevel(int level = 0); 87 | }; 88 | -------------------------------------------------------------------------------- /data/shaders/mesh_texture_cube.wgsl: -------------------------------------------------------------------------------- 1 | #include mesh_includes.wgsl 2 | #include tonemappers.wgsl 3 | 4 | #define GAMMA_CORRECTION 5 | 6 | @group(0) @binding(0) var mesh_data : InstanceData; 7 | 8 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 9 | 10 | @group(2) @binding(0) var irradiance_texture: texture_cube; 11 | @group(2) @binding(1) var albedo: vec4f; 12 | @group(2) @binding(7) var sampler_clamp : sampler; 13 | 14 | struct SkyboxVertexOutput { 15 | @builtin(position) position: vec4f, 16 | @location(0) vertex_position: vec3f, 17 | @location(1) world_position: vec3f, 18 | @location(2) color: vec4f 19 | }; 20 | 21 | 22 | @vertex 23 | fn vs_main(in: VertexInput) -> SkyboxVertexOutput { 24 | 25 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 26 | 27 | var out: SkyboxVertexOutput; 28 | var world_position = instance_data.model * vec4f(in.position, 1.0); 29 | out.world_position = world_position.xyz; 30 | out.position = camera_data.view_projection * world_position; 31 | out.vertex_position = in.position; 32 | out.color = vec4(in.color, 1.0) * albedo; 33 | return out; 34 | } 35 | 36 | struct FragmentOutput { 37 | @location(0) color: vec4f 38 | } 39 | 40 | @fragment 41 | fn fs_main(in: SkyboxVertexOutput) -> FragmentOutput { 42 | 43 | var view = normalize(in.world_position - camera_data.eye); 44 | 45 | var out: FragmentOutput; 46 | var final_color : vec3f = textureSampleLevel(irradiance_texture, sampler_clamp, in.vertex_position, 1.0).rgb * camera_data.ibl_intensity; 47 | 48 | // White furnace test 49 | // final_color = vec3f(1.0); 50 | 51 | final_color *= camera_data.exposure; 52 | final_color = tonemap_khronos_pbr_neutral(final_color); 53 | 54 | if (GAMMA_CORRECTION == 1) { 55 | final_color = pow(final_color, vec3(1.0 / 2.2)); 56 | } 57 | 58 | out.color = vec4f(final_color, 1.0); 59 | 60 | return out; 61 | } -------------------------------------------------------------------------------- /docs/reference/rendering-pipeline-desc.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/graphics/primitives/cone_mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "cone_mesh.h" 2 | 3 | #include "framework/math/math_utils.h" 4 | #include "framework/colors.h" 5 | 6 | #include "graphics/renderer_storage.h" 7 | 8 | #include "spdlog/spdlog.h" 9 | #include "imgui.h" 10 | 11 | ConeMesh::ConeMesh(float radius, float height, uint32_t segments, const glm::vec3& color) 12 | : PrimitiveMesh(color), radius(radius), height(height), segments(segments) 13 | { 14 | mesh_type = "ConeMesh"; 15 | 16 | build_mesh(); 17 | } 18 | 19 | void ConeMesh::build_mesh() 20 | { 21 | surface->create_cone(radius, height, segments, color); 22 | } 23 | 24 | void ConeMesh::set_radius(float new_radius) 25 | { 26 | radius = new_radius; 27 | build_mesh(); 28 | } 29 | 30 | void ConeMesh::set_height(float new_height) 31 | { 32 | height = new_height; 33 | build_mesh(); 34 | } 35 | 36 | void ConeMesh::set_segments(uint32_t new_segments) 37 | { 38 | segments = new_segments; 39 | build_mesh(); 40 | } 41 | 42 | void ConeMesh::render_gui() 43 | { 44 | ImGui::Text("Radius"); 45 | ImGui::SameLine(200); 46 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 47 | if (ImGui::DragFloat("##Radius", &radius, 0.01f, 0.01f, 2.0f)) { 48 | dirty = true; 49 | } 50 | 51 | ImGui::Text("Height"); 52 | ImGui::SameLine(200); 53 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 54 | if (ImGui::DragFloat("##Height", &height, 0.01f, 0.01f, 2.0f)) { 55 | dirty = true; 56 | } 57 | 58 | ImGui::Text("Segments"); 59 | ImGui::SameLine(200); 60 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 61 | int seg_int = static_cast(segments); 62 | if (ImGui::DragInt("##Segments", &seg_int, 1, 3, 64)) { 63 | segments = static_cast(seg_int); 64 | dirty = true; 65 | } 66 | 67 | if (dirty) { 68 | build_mesh(); 69 | dirty = false; 70 | } 71 | 72 | PrimitiveMesh::render_gui(); 73 | } 74 | -------------------------------------------------------------------------------- /docs/reference/scene.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/graphics/primitives/box_mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "box_mesh.h" 2 | 3 | #include "framework/math/math_utils.h" 4 | #include "framework/colors.h" 5 | 6 | #include "graphics/renderer_storage.h" 7 | 8 | #include "spdlog/spdlog.h" 9 | #include "imgui.h" 10 | 11 | BoxMesh::BoxMesh(float width, float height, float depth, const glm::vec3& color) 12 | : PrimitiveMesh(color), width(width), height(height), depth(depth) 13 | { 14 | mesh_type = "BoxMesh"; 15 | 16 | build_mesh(); 17 | } 18 | 19 | void BoxMesh::build_mesh() 20 | { 21 | surface->create_box(width, height, depth, color); 22 | } 23 | 24 | void BoxMesh::set_width(float new_width) 25 | { 26 | width = new_width; 27 | build_mesh(); 28 | } 29 | 30 | void BoxMesh::set_height(float new_height) 31 | { 32 | height = new_height; 33 | build_mesh(); 34 | } 35 | 36 | void BoxMesh::set_depth(float new_depth) 37 | { 38 | depth = new_depth; 39 | build_mesh(); 40 | } 41 | 42 | void BoxMesh::set_size(const glm::vec3& size) 43 | { 44 | width = size.x; 45 | height = size.y; 46 | depth = size.z; 47 | build_mesh(); 48 | } 49 | 50 | void BoxMesh::render_gui() 51 | { 52 | ImGui::Text("Width"); 53 | ImGui::SameLine(200); 54 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 55 | if (ImGui::DragFloat("##Width", &width, 0.01f, 0.01f, 2.0f)) { 56 | dirty = true; 57 | } 58 | 59 | ImGui::Text("Height"); 60 | ImGui::SameLine(200); 61 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 62 | if (ImGui::DragFloat("##Height", &height, 0.01f, 0.01f, 2.0f)) { 63 | dirty = true; 64 | } 65 | 66 | ImGui::Text("Depth"); 67 | ImGui::SameLine(200); 68 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 69 | if (ImGui::DragFloat("##Depth", &depth, 0.01f, 0.01f, 2.0f)) { 70 | dirty = true; 71 | } 72 | 73 | if (dirty) { 74 | build_mesh(); 75 | dirty = false; 76 | } 77 | 78 | PrimitiveMesh::render_gui(); 79 | } 80 | -------------------------------------------------------------------------------- /data/shaders/ui/ui_ray_pointer.wgsl: -------------------------------------------------------------------------------- 1 | #include ../mesh_includes.wgsl 2 | 3 | #define GAMMA_CORRECTION 4 | 5 | @group(0) @binding(0) var mesh_data : InstanceData; 6 | 7 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 8 | 9 | @group(2) @binding(1) var albedo: vec4f; 10 | 11 | @vertex 12 | fn vs_main(in: VertexInput) -> VertexOutput { 13 | 14 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 15 | 16 | var out: VertexOutput; 17 | var world_position = instance_data.model * vec4f(in.position, 1.0); 18 | out.world_position = world_position.xyz; 19 | out.position = camera_data.view_projection * world_position; 20 | out.uv = in.uv; // forward to the fragment shader 21 | out.color = vec4(vec3f(abs(in.position.z)), 1.0) * albedo; 22 | out.normal = in.normal; 23 | return out; 24 | } 25 | 26 | struct FragmentOutput { 27 | @location(0) color: vec4f 28 | } 29 | 30 | const COLOR_PRIMARY = pow(vec3f(0.976, 0.976, 0.976), vec3f(2.2)); 31 | const COLOR_SECONDARY = pow(vec3f(0.967, 0.892, 0.793), vec3f(2.2)); 32 | const COLOR_TERCIARY = pow(vec3f(1.0, 0.404, 0.0), vec3f(2.2)); 33 | const COLOR_HIGHLIGHT_LIGHT = pow(vec3f(0.467, 0.333, 0.933), vec3f(2.2)); 34 | const COLOR_HIGHLIGHT = pow(vec3f(0.26, 0.2, 0.533), vec3f(2.2)); 35 | const COLOR_HIGHLIGHT_DARK = pow(vec3f(0.082, 0.086, 0.196), vec3f(2.2)); 36 | const COLOR_DARK = pow(vec3f(0.172, 0.172, 0.172), vec3f(2.2)); 37 | 38 | @fragment 39 | fn fs_main(in: VertexOutput) -> FragmentOutput { 40 | 41 | var out: FragmentOutput; 42 | var dummy = camera_data.eye; 43 | 44 | var y : f32 = abs(in.color.r * 2.0 - 1.0); 45 | var f : f32 = 1.0 - clamp(pow(y, 0.5), 0.0, 1.0); 46 | 47 | var color : vec3f = mix( COLOR_TERCIARY, COLOR_HIGHLIGHT_LIGHT * 1.25, in.color.r ); 48 | 49 | if (GAMMA_CORRECTION == 1) { 50 | color = pow(color, vec3f(1.0 / 2.2)); 51 | } 52 | 53 | out.color = vec4f(color, f); 54 | 55 | return out; 56 | } -------------------------------------------------------------------------------- /src/framework/nodes/environment_3d.cpp: -------------------------------------------------------------------------------- 1 | #include "environment_3d.h" 2 | 3 | #include "framework/parsers/parse_obj.h" 4 | #include "framework/nodes/node_factory.h" 5 | 6 | #include "graphics/renderer.h" 7 | #include "graphics/renderer_storage.h" 8 | 9 | #include "shaders/mesh_texture_cube.wgsl.gen.h" 10 | 11 | REGISTER_NODE_CLASS(Environment3D) 12 | 13 | Environment3D::Environment3D() : MeshInstance3D() 14 | { 15 | node_type = "Environment3D"; 16 | 17 | name = "Environment3D"; 18 | 19 | Material* material = new Material(); 20 | material->set_diffuse_texture(Renderer::instance->get_irradiance_texture()); 21 | material->set_cull_type(CULL_BACK); 22 | material->set_type(MATERIAL_UNLIT); 23 | material->set_depth_write(false); 24 | material->set_priority(20); 25 | material->set_shader(RendererStorage::get_shader_from_source(shaders::mesh_texture_cube::source, shaders::mesh_texture_cube::path, shaders::mesh_texture_cube::libraries, material)); 26 | 27 | Surface* surface = new Surface(); 28 | surface->create_skybox(); 29 | surface->set_material(material); 30 | 31 | add_surface(surface); 32 | set_frustum_culling_enabled(false); 33 | } 34 | 35 | void Environment3D::update(float delta_time) 36 | { 37 | Node3D::update(delta_time); 38 | 39 | Renderer* renderer = static_cast(Renderer::instance); 40 | set_position(renderer->get_camera_eye()); 41 | } 42 | 43 | Texture* Environment3D::get_texture() const 44 | { 45 | Material* material = mesh->get_surface_material(0); 46 | return material->get_diffuse_texture(); 47 | } 48 | 49 | void Environment3D::set_texture(const std::string& texture_path) 50 | { 51 | Renderer* renderer = static_cast(Renderer::instance); 52 | set_position(renderer->get_camera_eye()); 53 | 54 | // Change irradiance first 55 | renderer->set_irradiance_texture(RendererStorage::get_texture(texture_path)); 56 | 57 | get_surface_material(0)->set_diffuse_texture(Renderer::instance->get_irradiance_texture()); 58 | } 59 | -------------------------------------------------------------------------------- /data/shaders/ui/ui_group.wgsl: -------------------------------------------------------------------------------- 1 | #include ui_includes.wgsl 2 | #include ../mesh_includes.wgsl 3 | 4 | #define GAMMA_CORRECTION 5 | 6 | @group(0) @binding(0) var mesh_data : InstanceData; 7 | 8 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 9 | 10 | @group(2) @binding(1) var albedo: vec4f; 11 | 12 | @group(3) @binding(0) var ui_data : UIData; 13 | 14 | @vertex 15 | fn vs_main(in: VertexInput) -> VertexOutput { 16 | 17 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 18 | 19 | var out: VertexOutput; 20 | var world_position = instance_data.model * vec4f(in.position, 1.0); 21 | out.world_position = world_position.xyz; 22 | out.position = camera_data.view_projection * world_position; 23 | out.uv = in.uv; // forward to the fragment shader 24 | out.color = vec4(in.color, 1.0) * albedo; 25 | out.normal = in.normal; 26 | return out; 27 | } 28 | 29 | struct FragmentOutput { 30 | @location(0) color: vec4f 31 | } 32 | 33 | @fragment 34 | fn fs_main(in: VertexOutput) -> FragmentOutput { 35 | 36 | var dummy = camera_data.eye; 37 | 38 | var out: FragmentOutput; 39 | 40 | // tr br tl bl 41 | var ra : vec4f = vec4f(0.98); 42 | var si : vec2f = vec2f(0.98 * ui_data.data_value, 0.98); 43 | ra = min(ra, min(vec4f(si.x), vec4f(si.y))); 44 | 45 | var uvs = vec2f(in.uv.x, 1.0 - in.uv.y); 46 | var pos : vec2f = vec2(uvs * 2.0 - 1.0); 47 | pos.x *= ui_data.aspect_ratio; 48 | 49 | let d : f32 = sdRoundedBox(pos, si, ra); 50 | 51 | var final_color : vec3f = in.color.rgb; 52 | 53 | if (GAMMA_CORRECTION == 1) { 54 | final_color = pow(final_color, vec3f(1.0 / 2.2)); 55 | } 56 | 57 | final_color = mix( final_color, vec3f(0.3), 1.0 - smoothstep(0.08, 0.1, abs(d)) ); 58 | 59 | var alpha : f32 = select(0.0, 1.0, d > 0.0); 60 | alpha = smoothstep(0.0, 0.04, d); 61 | 62 | out.color = vec4f(final_color, 1.0 - alpha); 63 | 64 | return out; 65 | } -------------------------------------------------------------------------------- /data/shaders/sdf_fonts.wgsl: -------------------------------------------------------------------------------- 1 | //https://medium.com/@calebfaith/implementing-msdf-font-in-opengl-ea09a9ab7e00 2 | 3 | #include mesh_includes.wgsl 4 | 5 | @group(0) @binding(0) var mesh_data : InstanceData; 6 | 7 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 8 | 9 | @group(2) @binding(0) var texture: texture_2d; 10 | @group(2) @binding(1) var albedo: vec4f; 11 | @group(2) @binding(7) var texture_sampler : sampler; 12 | 13 | @vertex 14 | fn vs_main(in: VertexInput) -> VertexOutput { 15 | 16 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 17 | 18 | var out: VertexOutput; 19 | var world_position = instance_data.model * vec4f(in.position, 1.0); 20 | out.world_position = world_position.xyz; 21 | out.position = camera_data.view_projection * world_position; 22 | out.uv = in.uv; // forward to the fragment shader 23 | out.color = vec4(in.color, 1.0) * albedo; 24 | out.normal = in.normal; 25 | return out; 26 | } 27 | 28 | struct FragmentOutput { 29 | @location(0) color: vec4f 30 | } 31 | 32 | fn median( r: f32, g: f32, b: f32) -> f32 { 33 | return max(min(r, g), min(max(r, g), b)); 34 | } 35 | 36 | fn screenPxRange( pxRange : f32, texCoord : vec2f ) -> f32 { 37 | var unitRange = vec2f(pxRange) / vec2f(textureDimensions(texture)); 38 | var screenTexSize = vec2f(1.0) / fwidth(texCoord); 39 | return max(0.5*dot(unitRange, screenTexSize), 1.0); 40 | } 41 | 42 | @fragment 43 | fn fs_main(in: VertexOutput) -> FragmentOutput { 44 | 45 | var dummy = camera_data.eye; 46 | 47 | var out: FragmentOutput; 48 | 49 | let bgColor : vec4f = vec4f(0.0, 0.0, 0.0, 0.0); 50 | let fgColor : vec4f = in.color; 51 | 52 | let msd : vec3f = textureSample(texture, texture_sampler, in.uv).rgb; 53 | let sd : f32 = median(msd.r, msd.g, msd.b); 54 | let screenPxDistance : f32 = screenPxRange(4.0, in.uv) * (sd - 0.3); 55 | let opacity : f32 = clamp(screenPxDistance + 0.3, 0.0, 1.0); 56 | out.color = mix(bgColor, fgColor, opacity); 57 | return out; 58 | } -------------------------------------------------------------------------------- /docs/reference/uniform.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/graphics/kernels/radix_sort_kernel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | #include "glm/glm.hpp" 5 | 6 | #include "graphics/pipeline.h" 7 | 8 | #include "prefix_sum_kernel.h" 9 | 10 | #include 11 | 12 | class RadixSortKernel { 13 | 14 | struct PipelineData { 15 | Pipeline pipeline; 16 | Uniform in_keys_uniform; 17 | Uniform out_keys_uniform; 18 | Uniform in_values_uniform; 19 | Uniform out_values_uniform; 20 | Uniform local_prefix_uniform; 21 | Uniform prefix_block_uniform; 22 | WGPUBindGroup bind_group; 23 | }; 24 | 25 | uint32_t count; 26 | uint32_t bit_count; 27 | glm::uvec2 workgroup_size; 28 | 29 | uint32_t keys_byte_size; 30 | uint32_t values_byte_size; 31 | 32 | uint32_t threads_per_workgroup; 33 | uint32_t workgroup_count; 34 | uint32_t prefix_block_workgroup_count; 35 | 36 | glm::uvec2 dispatch_size; 37 | 38 | Shader* block_sum_shader; 39 | Shader* reorder_shader; 40 | 41 | PrefixSumKernel* prefix_sum_kernel = nullptr; 42 | 43 | WGPUBuffer keys_buffer; 44 | WGPUBuffer values_buffer; 45 | WGPUBuffer prefix_block_sum_buffer; 46 | 47 | WGPUBuffer tmp_keys_buffer; 48 | WGPUBuffer tmp_values_buffer; 49 | WGPUBuffer local_prefix_sum_buffer; 50 | 51 | std::vector pipelines; 52 | 53 | void create_pipelines(); 54 | void create_prefix_sum_kernel(); 55 | 56 | void calculate_dispatch_sizes(); 57 | 58 | void create_buffers(); 59 | 60 | PipelineData* create_block_sum_pipeline(WGPUBuffer in_keys, uint8_t bit); 61 | PipelineData* create_reorder_pipeline(WGPUBuffer in_keys, WGPUBuffer in_values, WGPUBuffer out_keys, WGPUBuffer out_values, uint8_t bit); 62 | 63 | public: 64 | 65 | RadixSortKernel(WGPUBuffer keys_buffer, uint32_t keys_byte_size, WGPUBuffer values_buffer, uint32_t values_byte_size, uint32_t count, uint32_t bit_count = 32, const glm::uvec2& workgroup_size = { 16, 16 }); 66 | ~RadixSortKernel(); 67 | 68 | void dispatch(WGPUComputePassEncoder compute_pass); 69 | }; 70 | -------------------------------------------------------------------------------- /docs/js-bindings.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/framework/camera/orbit_camera.cpp: -------------------------------------------------------------------------------- 1 | #include "orbit_camera.h" 2 | 3 | #include "framework/input.h" 4 | 5 | #include "glm/gtx/norm.hpp" 6 | 7 | OrbitCamera::OrbitCamera() : Camera3D() 8 | { 9 | speed = 0.005f; 10 | } 11 | 12 | void OrbitCamera::update(float delta_time) 13 | { 14 | bool pan_enabled = Input::is_mouse_pressed(GLFW_MOUSE_BUTTON_RIGHT) || Input::is_mouse_pressed(GLFW_MOUSE_BUTTON_MIDDLE); 15 | 16 | glm::vec3 new_forward; 17 | 18 | if (!pan_enabled) { 19 | Camera3D::update(delta_time); 20 | 21 | new_forward = glm::normalize(yaw_pitch_to_vector(delta_yaw_lerp.value, delta_pitch_lerp.value)); 22 | 23 | } else { 24 | glm::vec2 mouse_delta = Input::get_mouse_delta(); 25 | 26 | new_forward = glm::normalize(yaw_pitch_to_vector(delta_yaw, delta_pitch)); 27 | 28 | glm::vec3 right = glm::normalize(glm::cross(new_forward, glm::vec3(0.0f, 1.0f, 0.0f))); 29 | glm::vec3 up = glm::normalize(glm::cross(new_forward, right)); 30 | 31 | right *= mouse_delta.x; 32 | up *= mouse_delta.y; 33 | 34 | eye += (right + up) * mouse_sensitivity; 35 | center += (right + up) * mouse_sensitivity; 36 | } 37 | 38 | float final_speed = speed; 39 | 40 | if (Input::is_key_pressed(GLFW_KEY_LEFT_SHIFT)) final_speed *= 10.0f; 41 | if (Input::is_key_pressed(GLFW_KEY_LEFT_CONTROL)) final_speed *= 0.1f; 42 | 43 | distance -= Input::get_mouse_wheel_delta() * final_speed; 44 | 45 | if (distance < 0.001f) { 46 | distance = 0.001f; 47 | } 48 | 49 | distance_lerp.value = smooth_damp(distance_lerp.value, distance, &distance_lerp.velocity, 0.1f, 50.0f, delta_time); 50 | 51 | look_at(center - new_forward * distance_lerp.value, center, glm::vec3(0.0f, 1.0f, 0.0f), false); 52 | } 53 | 54 | void OrbitCamera::look_at(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up, bool reset_internals) 55 | { 56 | Camera3D::look_at(eye, center, up, reset_internals); 57 | 58 | if (reset_internals) { 59 | distance = glm::length(eye - center); 60 | distance_lerp.value = distance; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/framework/ui/io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | 5 | #include "framework/nodes/node_2d.h" 6 | 7 | class Node2D; 8 | 9 | class IO { 10 | 11 | static std::vector> frame_inputs; 12 | 13 | static bool want_capture_input; 14 | 15 | static float xr_ray_distance; 16 | static float last_xr_ray_distance; 17 | 18 | static Node2D* focused; 19 | static Node2D* hovered; 20 | 21 | static glm::vec2 xr_position; 22 | static glm::vec3 xr_world_position; 23 | 24 | public: 25 | 26 | static void initialize(); 27 | static void start_frame(); 28 | static void end_frame(); 29 | static void update(float delta_time); 30 | 31 | static void set_xr_ray_distance(float d) { xr_ray_distance = d; }; 32 | static void set_xr_position(const glm::vec2& p) { xr_position = p; }; 33 | static void set_xr_world_position(const glm::vec3& p) { xr_world_position = p; }; 34 | 35 | static void set_focus(Node2D* node); 36 | static void set_hover(Node2D* node, const sInputData& data); 37 | 38 | static void set_want_capture_input(bool value) { want_capture_input = value; }; 39 | 40 | static void blur(); 41 | 42 | static bool is_hover_disabled(); 43 | 44 | static bool is_focus_type(uint32_t type); 45 | static bool is_focused(Node2D* node); 46 | static bool any_focus(); 47 | 48 | static bool is_hovered(Node2D* node); 49 | static bool is_hover_type(uint32_t type, uint32_t flag = 0u); 50 | static bool is_any_hover_type(const std::vector& types); 51 | static bool any_hover(); 52 | 53 | static bool get_want_capture_input(); 54 | static Node2D* get_focus() { return focused; } 55 | static Node2D* get_hover() { return hovered; } 56 | 57 | static const glm::vec2& get_xr_position() { return xr_position; } 58 | static const glm::vec3& get_xr_world_position() { return xr_world_position; } 59 | static const float get_xr_ray_distance() { return last_xr_ray_distance; } 60 | 61 | /* 62 | * Input management 63 | */ 64 | 65 | static void push_input(Node2D* node, sInputData data); 66 | }; 67 | -------------------------------------------------------------------------------- /docs/disk-storage.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/framework/animation/solvers/ik_solver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/math/transform.h" 4 | 5 | #include 6 | 7 | class IKSolver { 8 | protected: 9 | 10 | Transform aux_parent; // in global 11 | std::vector ik_chain; 12 | std::vector joint_indices; 13 | std::vector joint_constraint_type; // type 14 | std::vector joint_constraint_value; // limit or axis (in case is limit, set vec3 as (limit, limit, limit)) 15 | 16 | uint32_t num_steps = 16u; 17 | float threshold = 0.00001f; 18 | 19 | public: 20 | 21 | enum constraints { 22 | BALL = 1, 23 | HINGE 24 | }; 25 | 26 | IKSolver() {} 27 | 28 | size_t size(); 29 | virtual void resize(size_t new_size); 30 | Transform& operator[](uint32_t index); 31 | 32 | std::vector& get_chain() { return ik_chain; } 33 | const std::vector& get_joint_indices() { return joint_indices; } 34 | uint32_t get_num_steps() { return num_steps; } 35 | float get_threshold() { return threshold; } 36 | const Transform& get_joint(uint32_t idx); 37 | Transform& get_local_transform(uint32_t index); 38 | Transform get_global_transform(uint32_t index); 39 | 40 | void set_chain(std::vector chain); 41 | void set_joint_indices(std::vector indices); 42 | void set_parent_transform(const Transform& t); 43 | void set_local_transform(uint32_t index, const Transform& t); 44 | void set_local_transform(uint32_t index, const Transform& t, uint32_t joint_idx); 45 | void set_global_transform(uint32_t index, const Transform& t); 46 | void set_num_steps(uint32_t new_num_steps) { num_steps = new_num_steps; } 47 | void set_threshold(float new_threshold) { threshold = new_threshold; } 48 | void set_ball_socket_constraint(uint32_t idx, float limit); 49 | void set_hinge_socket_constraint(uint32_t idx, glm::vec3 axis); 50 | 51 | void apply_ball_socket_constraint(int i, float limit_angle); 52 | void apply_hinge_socket_constraint(int i, glm::vec3 axis); 53 | 54 | virtual bool solve(const Transform& target) { return false; }; 55 | }; 56 | 57 | -------------------------------------------------------------------------------- /src/framework/nodes/skeleton_helper_3d.cpp: -------------------------------------------------------------------------------- 1 | #include "skeleton_helper_3d.h" 2 | 3 | #include "graphics/renderer_storage.h" 4 | 5 | #include "framework/animation/skeleton.h" 6 | 7 | #include "shaders/mesh_forward.wgsl.gen.h" 8 | 9 | #include 10 | 11 | SkeletonHelper3D::SkeletonHelper3D(Skeleton* new_skeleton, Node3D* parent) : MeshInstance3D() 12 | { 13 | node_type = "SkeletonHelper3D"; 14 | 15 | skeleton = new_skeleton; 16 | 17 | if (parent) { 18 | set_parent(parent); 19 | } 20 | } 21 | 22 | void SkeletonHelper3D::initialize() 23 | { 24 | Surface* s = new Surface(); 25 | s->set_name("Skeleton Helper"); 26 | add_surface(s); 27 | 28 | set_frustum_culling_enabled(false); 29 | 30 | inner_update(); 31 | 32 | Material* skeleton_material = new Material(); 33 | skeleton_material->set_color({ 1.0f, 0.0f, 0.0f, 1.0f }); 34 | skeleton_material->set_depth_read(false); 35 | skeleton_material->set_priority(0); 36 | skeleton_material->set_topology_type(eTopologyType::TOPOLOGY_LINE_LIST); 37 | skeleton_material->set_shader(RendererStorage::get_shader_from_source(shaders::mesh_forward::source, shaders::mesh_forward::path, shaders::mesh_forward::libraries, skeleton_material)); 38 | set_surface_material_override(s, skeleton_material); 39 | } 40 | 41 | void SkeletonHelper3D::inner_update() 42 | { 43 | Surface* s = get_surface(0); 44 | 45 | sSurfaceData vertices; 46 | 47 | Pose& pose = skeleton->get_current_pose(); 48 | size_t joint_count = pose.size(); 49 | 50 | vertices.vertices.reserve(joint_count); 51 | 52 | for (size_t i = 0; i < joint_count; ++i) { 53 | vertices.vertices.push_back(pose.get_global_transform(i).get_position()); 54 | if (pose.get_parent(i) >= 0) { 55 | vertices.vertices.push_back(pose.get_global_transform(pose.get_parent(i)).get_position()); 56 | } 57 | else { 58 | vertices.vertices.push_back(pose.get_global_transform(i).get_position()); 59 | } 60 | } 61 | 62 | s->update_surface_data(vertices); 63 | } 64 | 65 | void SkeletonHelper3D::update(float dt) 66 | { 67 | inner_update(); 68 | } 69 | -------------------------------------------------------------------------------- /src/graphics/primitives/sphere_mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "sphere_mesh.h" 2 | 3 | #include "framework/math/math_utils.h" 4 | #include "framework/colors.h" 5 | 6 | #include "graphics/renderer_storage.h" 7 | 8 | #include "spdlog/spdlog.h" 9 | #include "imgui.h" 10 | 11 | SphereMesh::SphereMesh(float radius, uint32_t rings, uint32_t ring_segments, const glm::vec3& color) 12 | : PrimitiveMesh(color), radius(radius), rings(rings), ring_segments(ring_segments) 13 | { 14 | mesh_type = "SphereMesh"; 15 | 16 | build_mesh(); 17 | } 18 | 19 | void SphereMesh::build_mesh() 20 | { 21 | surface->create_sphere(radius, ring_segments, rings, color); 22 | } 23 | 24 | void SphereMesh::set_radius(float new_radius) 25 | { 26 | radius = new_radius; 27 | build_mesh(); 28 | } 29 | 30 | void SphereMesh::set_rings(uint32_t new_rings) 31 | { 32 | rings = new_rings; 33 | build_mesh(); 34 | } 35 | 36 | void SphereMesh::set_ring_segments(uint32_t new_ring_segments) 37 | { 38 | ring_segments = new_ring_segments; 39 | build_mesh(); 40 | } 41 | 42 | void SphereMesh::render_gui() 43 | { 44 | ImGui::Text("Radius"); 45 | ImGui::SameLine(200); 46 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 47 | if (ImGui::DragFloat("##Radius", &radius, 0.01f, 0.01f, 2.0f)) { 48 | dirty = true; 49 | } 50 | 51 | ImGui::Text("Rings"); 52 | ImGui::SameLine(200); 53 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 54 | int rings_int = static_cast(rings); 55 | if (ImGui::DragInt("##Rings", &rings_int, 1, 1, 64)) { 56 | rings = static_cast(rings_int); 57 | dirty = true; 58 | } 59 | 60 | ImGui::Text("Ring Segments"); 61 | ImGui::SameLine(200); 62 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 63 | int ring_seg_int = static_cast(ring_segments); 64 | if (ImGui::DragInt("##Ring Segments", &ring_seg_int, 1, 4, 64)) { 65 | ring_segments = static_cast(ring_seg_int); 66 | dirty = true; 67 | } 68 | 69 | if (dirty) { 70 | build_mesh(); 71 | dirty = false; 72 | } 73 | 74 | PrimitiveMesh::render_gui(); 75 | } 76 | -------------------------------------------------------------------------------- /data/shaders/brdf_lut_gen.wgsl: -------------------------------------------------------------------------------- 1 | #include pbr_functions.wgsl 2 | 3 | @group(0) @binding(0) var brdf_lut: texture_storage_2d; 4 | 5 | const SAMPLE_COUNT = 1024u; 6 | const INV_SAMPLE_COUNT = 1.0 / f32(SAMPLE_COUNT); 7 | 8 | const TEXTURE_WIDTH = 512.0; 9 | const TEXTURE_HEIGHT = 512.0; 10 | 11 | fn geometrySchlickGGXforIBL(NdotV : f32, k : f32) -> f32 12 | { 13 | return NdotV / (NdotV * (1.0 - k) + k); 14 | } 15 | 16 | fn geometrySmithIBL(NdotL : f32, NdotV : f32, roughness : f32) -> f32 17 | { 18 | let k : f32 = (roughness * roughness) / 2.0; 19 | let ggx2 : f32 = geometrySchlickGGXforIBL(NdotV, k); 20 | let ggx1 : f32 = geometrySchlickGGXforIBL(NdotL, k); 21 | 22 | return ggx1 * ggx2; 23 | } 24 | 25 | // https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/main/source/shaders/ibl_filtering.frag#L364 26 | fn integrate_BRDF(in_NdotV : f32, roughness : f32) -> vec2f 27 | { 28 | let NdotV : f32 = max(in_NdotV, 0.001); // prevent nans 29 | 30 | let V : vec3f = vec3f(sqrt(1.0 - NdotV * NdotV), 0.0, NdotV); 31 | 32 | let N : vec3f = vec3f(0.0, 0.0, 1.0); 33 | 34 | var A : f32 = 0.0; 35 | var B : f32 = 0.0; 36 | 37 | for(var i : u32 = 0u; i < SAMPLE_COUNT; i = i + 1u) 38 | { 39 | let Xi : vec2f = Hammersley(i, SAMPLE_COUNT); 40 | let H : vec3f = importance_sample_GGX(Xi, roughness); 41 | let L : vec3f = normalize(reflect(-V, H)); 42 | 43 | let NdotL : f32 = max(L.z, 0.0); 44 | let NdotH : f32 = max(H.z, 0.0); 45 | let VdotH : f32 = max(dot(V, H), 0.0); 46 | 47 | if (NdotL > 0.0) 48 | { 49 | let V_pdf : f32 = geometrySmithIBL(NdotL, NdotV, roughness); 50 | let V_pdf_vis : f32 = (V_pdf * VdotH) / (NdotH * NdotV); 51 | let Fc : f32 = pow(1.0 - VdotH, 5.0); 52 | 53 | A += (1.0 - Fc) * V_pdf_vis; 54 | B += Fc * V_pdf_vis; 55 | } 56 | } 57 | 58 | return vec2(A, B) * INV_SAMPLE_COUNT; 59 | } 60 | 61 | @compute @workgroup_size(16, 16, 1) 62 | fn compute(@builtin(global_invocation_id) id: vec3) 63 | { 64 | textureStore(brdf_lut, id.xy, vec4f(integrate_BRDF(f32(id.x) / TEXTURE_WIDTH, f32(id.y) / TEXTURE_HEIGHT), 0.0, 0.0)); 65 | } 66 | -------------------------------------------------------------------------------- /src/framework/nodes/animation_player.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "node_3d.h" 4 | #include "framework/animation/blend_animation.h" 5 | 6 | #include "framework/ui/imgui_timeline.h" 7 | 8 | class MeshInstance3D; 9 | 10 | class AnimationPlayer : public Node3D 11 | { 12 | std::string current_animation_name; 13 | 14 | bool autoplay = false; 15 | bool playing = false; 16 | bool paused = false; 17 | 18 | float playback = 0.f; 19 | float speed = 1.f; 20 | float blend_time = 0.f; 21 | 22 | uint8_t blend_type = ANIMATION_BLEND_CROSSFADE; 23 | uint8_t loop_type = ANIMATION_LOOP_DEFAULT; 24 | 25 | BlendAnimation blender; 26 | ImGuiTimeline imgui_timeline; 27 | 28 | std::vector track_data; 29 | int selected_track = -1; 30 | 31 | void generate_track_data(); 32 | 33 | #ifdef __EMSCRIPTEN__ 34 | public: 35 | #endif 36 | Node3D* root_node = nullptr; 37 | 38 | public: 39 | 40 | AnimationPlayer(); 41 | AnimationPlayer(const std::string& name); 42 | 43 | void play(Animation* animation = nullptr, float start_time = 0.0f, float custom_blend = -1.0f, float custom_speed = 1.0f); 44 | void play(const std::string& animation_name = "", float start_time = 0.0f, float custom_blend = -1.0f, float custom_speed = 1.0f); 45 | void pause(); 46 | void resume(); 47 | void stop(bool keep_state = false); 48 | 49 | void update(float delta_time) override; 50 | void render_gui() override; 51 | 52 | void set_playback_time(float time); 53 | void set_speed(float new_speed) { speed = new_speed; } 54 | void set_blend_time(float new_blend_time) { blend_time = new_blend_time; } 55 | void set_loop_type(uint8_t new_loop_type) { loop_type = new_loop_type; } 56 | void set_root_node(Node3D* new_root_node) { root_node = new_root_node; } 57 | 58 | float get_playback_time() const { return playback; } 59 | float get_blend_time() const { return blend_time; } 60 | float get_speed() const { return speed; } 61 | uint8_t get_loop_type() const { return loop_type; } 62 | Node3D* get_root_node() const { return root_node; } 63 | bool is_playing() const { return playing; } 64 | bool is_paused() const { return paused; } 65 | }; 66 | 67 | -------------------------------------------------------------------------------- /src/framework/input_xr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | #ifdef OPENXR_SUPPORT 5 | #include "xr/dawnxr/dawnxr.h" 6 | #endif 7 | 8 | #include "glm/vec3.hpp" 9 | #include "glm/gtc/quaternion.hpp" 10 | 11 | #include 12 | #include 13 | 14 | #ifdef OPENXR_SUPPORT 15 | 16 | struct XrActionStorage { 17 | bool active = false; 18 | XrPath path; 19 | XrAction action; 20 | XrActionStateBoolean state; 21 | }; 22 | 23 | struct XrMappedButtonState { 24 | std::string name; 25 | uint8_t hand; 26 | XrActionStorage click; 27 | XrActionStorage touch; 28 | 29 | void bind_click(XrInstance* instance, const char* path) { click.active = true; xrStringToPath(*instance, path, &click.path); } 30 | void bind_touch(XrInstance* instance, const char* path) { touch.active = true; xrStringToPath(*instance, path, &touch.path); } 31 | }; 32 | 33 | struct 34 | { 35 | bool supported = false; 36 | // whether the current VR system in use has hand tracking 37 | bool system_supported = false; 38 | PFN_xrLocateHandJointsEXT pfnLocateHandJointsEXT = {}; 39 | std::array trackers = {}; 40 | } hand_tracking; 41 | 42 | struct sInputState { 43 | 44 | XrActionSet actionSet{ XR_NULL_HANDLE }; 45 | 46 | // hand pose: point in the world using the input source, according to the platform's conventions for aiming with that kind of source. 47 | XrAction aimPoseAction{ XR_NULL_HANDLE }; 48 | // hand pose: render a virtual object held in the user's hand, whether it is tracked directly or by a motion controller. 49 | XrAction gripPoseAction{ XR_NULL_HANDLE }; 50 | 51 | XrAction grabAction{ XR_NULL_HANDLE }; 52 | XrAction thumbstickValueAction{ XR_NULL_HANDLE }; 53 | XrAction thumbstickClickAction{ XR_NULL_HANDLE }; 54 | XrAction thumbstickTouchAction{ XR_NULL_HANDLE }; 55 | XrAction triggerValueAction{ XR_NULL_HANDLE }; 56 | XrAction triggerTouchAction{ XR_NULL_HANDLE }; 57 | XrAction vibrateAction{ XR_NULL_HANDLE }; 58 | 59 | // Buttons. 60 | // There are stored in data input... 61 | 62 | XrPath handSubactionPath[HAND_COUNT]; 63 | XrSpace aimHandSpace[HAND_COUNT]; 64 | XrSpace gripHandSpace[HAND_COUNT]; 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/graphics/debug/renderdoc_capture.cpp: -------------------------------------------------------------------------------- 1 | #include "renderdoc_capture.h" 2 | 3 | #if defined(_WIN32) 4 | 5 | #include "windows.h" 6 | 7 | #elif defined(__linux__) 8 | 9 | #include 10 | 11 | #endif 12 | 13 | //#include "spdlog/spdlog.h" 14 | 15 | #ifndef __EMSCRIPTEN__ 16 | RENDERDOC_API_1_6_0* RenderdocCapture::rdoc_api = nullptr; 17 | #endif 18 | 19 | bool RenderdocCapture::capture_started = false; 20 | 21 | void RenderdocCapture::init() 22 | { 23 | bool loaded = false; 24 | 25 | #if defined(_WIN32) 26 | 27 | // At init, on windows 28 | if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) 29 | { 30 | pRENDERDOC_GetAPI RENDERDOC_GetAPI = 31 | (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI"); 32 | loaded = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api) == 1; 33 | } 34 | 35 | #elif defined(__linux__) 36 | 37 | // At init, on linux/android. 38 | // For android replace librenderdoc.so with libVkLayer_GLES_RenderDoc.so 39 | if (void* mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD)) 40 | { 41 | pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI"); 42 | loaded = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void**)&rdoc_api) == 1; 43 | } 44 | 45 | #endif 46 | 47 | //if (!loaded) { 48 | // spdlog::warn("Could not initialize Renderdoc Capture. To capture, start the program from Renderdoc"); 49 | //} 50 | //else { 51 | // spdlog::info("Renderdoc Capture initialized"); 52 | //} 53 | } 54 | 55 | void RenderdocCapture::start_capture_frame() 56 | { 57 | #ifndef __EMSCRIPTEN__ 58 | if (rdoc_api) { 59 | rdoc_api->StartFrameCapture(nullptr, nullptr); 60 | capture_started = true; 61 | } 62 | else { 63 | //spdlog::error("Can not start frame capture, Renderdoc Capture not initialized"); 64 | } 65 | #endif 66 | } 67 | 68 | void RenderdocCapture::end_capture_frame() 69 | { 70 | #ifndef __EMSCRIPTEN__ 71 | if (rdoc_api) { 72 | rdoc_api->EndFrameCapture(nullptr, nullptr); 73 | capture_started = false; 74 | } 75 | #endif 76 | } 77 | 78 | bool RenderdocCapture::is_capture_started() 79 | { 80 | return capture_started; 81 | } 82 | -------------------------------------------------------------------------------- /src/framework/animation/animation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "track.h" 4 | 5 | #include "framework/resources/resource.h" 6 | 7 | #include 8 | 9 | enum eAnimationType { 10 | ANIMATION_TYPE_UNDEFINED, 11 | ANIMATION_TYPE_SIMPLE, 12 | ANIMATION_TYPE_SKELETON, 13 | }; 14 | 15 | enum eBlendType : uint8_t { 16 | ANIMATION_BLEND_CROSSFADE, 17 | ANIMATION_BLEND_ADDITIVE 18 | }; 19 | 20 | class Animation : public Resource { 21 | 22 | static uint32_t last_animation_id; 23 | 24 | std::vector tracks; 25 | std::string name; 26 | 27 | float start_time = 0.0f; 28 | float end_time = 0.0f; 29 | float duration = 0.0f; 30 | 31 | bool reversed = false; 32 | 33 | float adjust_time_to_fit_range(float time, uint8_t loop); 34 | 35 | eAnimationType type = ANIMATION_TYPE_UNDEFINED; 36 | 37 | public: 38 | 39 | Animation(); 40 | ~Animation() {}; 41 | 42 | // Gets joint Id based for a specific track index 43 | int get_id_at_index(uint32_t index); 44 | 45 | // Sets joint ID based on the index of the joint in the clip 46 | void set_id_at_index(uint32_t index, int id); 47 | 48 | // Samples the animation clip at the provided time into the out reference 49 | float sample(float time, uint32_t track_idx, uint8_t loop, Node::AnimatableProperty* out = nullptr, eInterpolationType interpolation_type = INTERPOLATION_UNSET); 50 | 51 | // Returns a transform track for the specified track position id 52 | Track* operator[](uint32_t index); 53 | 54 | // Sets the start/end time of the animation clip based on the tracks that make up the clip 55 | void recalculate_duration(); 56 | 57 | // Adds a new track 58 | Track* add_track(int id = -1); 59 | 60 | uint32_t get_track_count(); 61 | Track* get_track_by_id(int id); 62 | Track* get_track(uint32_t index); 63 | std::string& get_name(); 64 | float get_duration(); 65 | float get_start_time(); 66 | float get_end_time(); 67 | eAnimationType get_type(); 68 | bool is_reversed(); 69 | 70 | void serialize(std::ofstream& binary_scene_file); 71 | void parse(std::ifstream& binary_scene_file); 72 | 73 | void set_type(eAnimationType new_type); 74 | void set_name(const std::string& new_name); 75 | }; 76 | -------------------------------------------------------------------------------- /src/framework/parsers/parse_scene.cpp: -------------------------------------------------------------------------------- 1 | #include "framework/parsers/parse_scene.h" 2 | 3 | #include "parse_obj.h" 4 | #include "parse_gltf.h" 5 | #include "parse_vdb.h" 6 | #include "parse_ply.h" 7 | 8 | #include "framework/nodes/mesh_instance_3d.h" 9 | 10 | #include "spdlog/spdlog.h" 11 | 12 | bool parse_scene(const char* scene_path, std::vector& entities, bool fill_surface_data, Node3D* root) 13 | { 14 | std::string scene_path_str = std::string(scene_path); 15 | std::string extension = scene_path_str.substr(scene_path_str.find_last_of(".") + 1); 16 | 17 | spdlog::info("Parsing scene: {}", scene_path); 18 | 19 | if (extension == "obj") { 20 | entities.push_back(parse_obj(scene_path)); 21 | return true; 22 | } 23 | 24 | Parser* parser = nullptr; 25 | uint32_t flags = 0u; 26 | 27 | if (extension == "gltf" || extension == "glb") { 28 | spdlog::info("Parsing a GLTF/GLB file"); 29 | parser = new GltfParser(); 30 | static_cast(parser)->push_scene_root(root); 31 | if (fill_surface_data) { 32 | flags |= PARSE_GLTF_FILL_SURFACE_DATA; 33 | } 34 | } 35 | else if (extension == "vdb") { 36 | spdlog::info("Parsing a VDB file (WIP)"); 37 | parser = new VdbParser(); 38 | } 39 | else if (extension == "ply") { 40 | spdlog::info("Parsing a PLY file"); 41 | parser = new PlyParser(); 42 | } 43 | else { 44 | spdlog::error("Scene extension .{} not supported", extension); 45 | assert(0); 46 | return false; 47 | } 48 | 49 | bool res = parser->parse(scene_path, entities, flags); 50 | 51 | delete parser; 52 | 53 | return res; 54 | } 55 | 56 | MeshInstance3D* parse_mesh(const char* mesh_path, bool create_aabb, bool fill_surface_data) 57 | { 58 | std::string mesh_path_str = std::string(mesh_path); 59 | std::string extension = mesh_path_str.substr(mesh_path_str.find_last_of(".") + 1); 60 | 61 | spdlog::trace("Parsing mesh: {}", mesh_path); 62 | 63 | if (extension == "obj") { 64 | return parse_obj(mesh_path, create_aabb); 65 | } 66 | else { 67 | spdlog::error("Mesh extension .{} not supported", extension); 68 | assert(0); 69 | } 70 | 71 | return {}; 72 | } 73 | -------------------------------------------------------------------------------- /src/framework/math/transform.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/vec3.hpp" 4 | #include "glm/vec4.hpp" 5 | #include "glm/gtc/quaternion.hpp" 6 | 7 | class Transform { 8 | 9 | // internal properties 10 | glm::vec3 position_p; 11 | glm::quat rotation_p; 12 | glm::vec3 scale_p; 13 | 14 | glm::mat4x4 model = glm::identity(); 15 | 16 | bool dirty = true; 17 | 18 | public: 19 | 20 | Transform(const glm::vec3& p, const glm::quat& r, const glm::vec3& s); 21 | Transform(); 22 | 23 | void set_position(const glm::vec3& position); 24 | void set_rotation(const glm::quat& rotation); 25 | void set_scale(const glm::vec3& scale); 26 | 27 | const glm::vec3& get_position() const; 28 | const glm::quat& get_rotation() const; 29 | const glm::vec3& get_scale() const; 30 | 31 | glm::vec3& get_position_ref(); 32 | glm::quat& get_rotation_ref(); 33 | glm::vec3& get_scale_ref(); 34 | 35 | void set_dirty(bool dirty); 36 | 37 | // true if it changed during this frame 38 | bool is_dirty(); 39 | 40 | void translate(const glm::vec3& translation); 41 | void rotate(const glm::quat& rotation_factor); 42 | void scale(const glm::vec3& scale_factor); 43 | 44 | void rotate_world(const glm::quat& rotation_delta); 45 | void rotate_arround(const glm::quat& rotation, const glm::vec3 pivot); 46 | 47 | void cache_model(); 48 | 49 | const glm::mat4x4& get_model(); 50 | const glm::mat4x4 get_model() const; 51 | 52 | glm::vec3 get_front() const; 53 | glm::vec3 get_up() const; 54 | 55 | static Transform identity(); 56 | static Transform combine(const Transform& a, const Transform& b); 57 | static Transform inverse(const Transform& t); 58 | static Transform mix(const Transform& a, const Transform& b, float t); 59 | static glm::mat4x4 transform_to_mat4(const Transform& t); 60 | static Transform mat4_to_transform(const glm::mat4x4& m); 61 | static glm::vec3 transform_point(const Transform& a, const glm::vec3& b); 62 | static glm::vec3 transform_vector(const Transform& a, const glm::vec3& b); 63 | static glm::quat get_rotation_between_vectors(const glm::vec3& from, const glm::vec3& to); 64 | static float get_angle_between_vectors(const glm::vec3& l, const glm::vec3& r); 65 | 66 | }; 67 | -------------------------------------------------------------------------------- /data/shaders/tonemappers.wgsl: -------------------------------------------------------------------------------- 1 | // Uncharted 2 tone map 2 | // see: http://filmicworlds.com/blog/filmic-tonemapping-operators/ 3 | fn tonemap_uncharted2_imp( color : vec3f ) -> vec3f 4 | { 5 | let A = 0.15; 6 | let B = 0.50; 7 | let C = 0.10; 8 | let D = 0.20; 9 | let E = 0.02; 10 | let F = 0.30; 11 | return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F; 12 | } 13 | 14 | fn tonemap_uncharted( c : vec3f ) -> vec3f 15 | { 16 | let W = 11.2; 17 | let color = tonemap_uncharted2_imp(c * 2.0); 18 | let whiteScale = 1.0 / tonemap_uncharted2_imp( vec3f(W) ); 19 | return color * whiteScale;//LINEARtoSRGB(color * whiteScale); 20 | } 21 | 22 | // https://github.com/godotengine/godot/blob/aa5b6ed13e4644633baf2a8a1384c82e91c533a1/servers/rendering/renderer_rd/shaders/effects/tonemap.glsl#L197 23 | fn tonemap_filmic(color : vec3f, white : f32) -> vec3f 24 | { 25 | let exposure_bias : f32 = 2.0; 26 | let A : f32 = 0.22 * exposure_bias * exposure_bias; // bias baked into constants for performance 27 | let B : f32 = 0.30 * exposure_bias; 28 | let C : f32 = 0.10; 29 | let D : f32 = 0.20; 30 | let E : f32 = 0.01; 31 | let F : f32 = 0.30; 32 | 33 | let color_tonemapped : vec3f = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; 34 | let white_tonemapped : f32 = ((white * (A * white + C * B) + D * E) / (white * (A * white + B) + D * F)) - E / F; 35 | 36 | return color_tonemapped / white_tonemapped; 37 | } 38 | 39 | fn tonemap_khronos_pbr_neutral( color : vec3f ) -> vec3f 40 | { 41 | var final_color : vec3f = color; 42 | 43 | let startCompression : f32 = 0.8 - 0.04; 44 | let desaturation : f32 = 0.15; 45 | 46 | let x : f32 = min(final_color.r, min(final_color.g, final_color.b)); 47 | let offset : f32 = select(0.04, x - 6.25 * x * x, x < 0.08); 48 | final_color -= offset; 49 | 50 | let peak : f32 = max(final_color.r, max(final_color.g, final_color.b)); 51 | if (peak < startCompression) { 52 | return final_color; 53 | } 54 | 55 | let d : f32 = 1. - startCompression; 56 | let newPeak : f32 = 1. - d * d / (peak + d - startCompression); 57 | final_color *= newPeak / peak; 58 | 59 | let g : f32 = 1. - 1. / (desaturation * (peak - newPeak) + 1.); 60 | return mix(final_color, vec3f(1.0, 1.0, 1.0), g); 61 | } -------------------------------------------------------------------------------- /src/framework/nodes/node_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/nodes/node.h" 4 | #include "framework/math/transform.h" 5 | 6 | #include 7 | #include 8 | 9 | enum eColliderShape { 10 | COLLIDER_SHAPE_NONE, 11 | COLLIDER_SHAPE_AABB, 12 | COLLIDER_SHAPE_SPHERE, 13 | COLLIDER_SHAPE_CAPSULE, 14 | COLLIDER_SHAPE_CUSTOM, 15 | }; 16 | 17 | class Node3D : public Node { 18 | 19 | protected: 20 | 21 | Transform transform = {}; 22 | 23 | eColliderShape collider_shape = COLLIDER_SHAPE_AABB; 24 | 25 | bool selected = false; 26 | 27 | public: 28 | 29 | Node3D(); 30 | virtual ~Node3D(); 31 | 32 | virtual void initialize() override {}; 33 | 34 | virtual void render() override; 35 | virtual void update(float delta_time) override; 36 | virtual void render_gui() override; 37 | 38 | void translate(const glm::vec3& translation); 39 | void rotate(float angle, const glm::vec3& axis); 40 | void rotate(const glm::quat& q); 41 | void rotate_world(const glm::quat& q); 42 | void scale(const glm::vec3& scale); 43 | 44 | virtual void serialize(std::ofstream& binary_scene_file) override; 45 | virtual void parse(std::ifstream& binary_scene_file) override; 46 | 47 | const glm::vec3 get_local_translation() const; 48 | const glm::vec3 get_translation(); 49 | virtual glm::mat4x4 get_global_model(); 50 | glm::mat4x4 get_model(); 51 | glm::quat get_rotation() const; 52 | Transform get_transform() const; 53 | Transform& get_transform(); 54 | virtual Transform get_global_transform(); 55 | 56 | virtual void set_position(const glm::vec3& translation); 57 | virtual void set_rotation(const glm::quat& rotation); 58 | virtual void set_scale(const glm::vec3& scale); 59 | virtual void set_global_transform(const Transform& new_transform); 60 | void set_transform(const Transform& new_transform); 61 | void set_transform_dirty(bool value); 62 | void set_parent(Node3D* node); 63 | 64 | virtual bool test_ray_collision(const glm::vec3& ray_origin, const glm::vec3& ray_direction, float* distance, Node3D** out = nullptr); 65 | 66 | void clone(Node* new_node, bool copy = true) override; 67 | 68 | void select(); 69 | void unselect(); 70 | 71 | bool is_selected(); 72 | bool is_child_selected(); 73 | }; 74 | -------------------------------------------------------------------------------- /src/framework/camera/camera_3d.cpp: -------------------------------------------------------------------------------- 1 | #include "camera_3d.h" 2 | 3 | #include "framework/input.h" 4 | #include "framework/nodes/node_3d.h" 5 | 6 | void Camera3D::apply_movement(const glm::vec2& movement) 7 | { 8 | { 9 | glm::vec2 delta = movement; 10 | 11 | delta.x = glm::clamp(delta.x, -150.0f, 150.0f); 12 | 13 | delta_yaw += delta.x * mouse_sensitivity; 14 | delta_pitch -= delta.y * mouse_sensitivity; 15 | } 16 | 17 | delta_yaw = clamp_rotation(delta_yaw); 18 | delta_pitch = clamp_rotation(delta_pitch); 19 | 20 | float max_offset = 0.25f; 21 | 22 | if (delta_pitch >= PI_2_F - max_offset && delta_pitch < PI) { 23 | delta_pitch = PI_2_F - 0.001f - max_offset; 24 | } 25 | 26 | if (delta_pitch > PI_F && delta_pitch <= 3.0f * PI_2_F + max_offset) { 27 | delta_pitch = 3.0f * PI_2_F + 0.001f + max_offset; 28 | } 29 | } 30 | 31 | void Camera3D::update(float delta_time) 32 | { 33 | if (Input::is_mouse_pressed(GLFW_MOUSE_BUTTON_LEFT)) 34 | { 35 | apply_movement(Input::get_mouse_delta()); 36 | } 37 | 38 | delta_pitch_lerp.value = smooth_damp_angle(delta_pitch_lerp.value, delta_pitch, &delta_pitch_lerp.velocity, 0.05f, 40.0f, delta_time); 39 | delta_yaw_lerp.value = smooth_damp_angle(delta_yaw_lerp.value, delta_yaw, &delta_yaw_lerp.velocity, 0.05f, 40.0f, delta_time); 40 | } 41 | 42 | void Camera3D::look_at(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up, bool reset_internals) 43 | { 44 | Camera::look_at(eye, center, up); 45 | 46 | if (reset_internals) { 47 | vector_to_yaw_pitch(glm::normalize(glm::vec3(center - eye)), &delta_yaw, &delta_pitch); 48 | delta_yaw_lerp.value = delta_yaw; 49 | delta_pitch_lerp.value = delta_pitch; 50 | eye_lerp.value = eye; 51 | } 52 | } 53 | 54 | void Camera3D::look_at_node(Node3D* entity) 55 | { 56 | if (!entity) { 57 | return; 58 | } 59 | 60 | glm::vec3 front = glm::vec3(0.0f, 0.0f, -1.0f); 61 | 62 | AABB aabb = entity->get_aabb(); 63 | 64 | float distance = 0.5f; 65 | 66 | if (aabb.initialized()) { 67 | distance = 3.0f * std::max(std::max(abs(aabb.half_size.x), abs(aabb.half_size.y)), abs(aabb.half_size.z)); 68 | } 69 | 70 | look_at(aabb.center - distance * front, aabb.center, glm::vec3(0.0, 1.0, 0.0)); 71 | } 72 | -------------------------------------------------------------------------------- /src/framework/utils/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graphics/material.h" 4 | #include "graphics/pipeline.h" 5 | 6 | #include "glm/gtx/hash.hpp" 7 | 8 | // based on: https://stackoverflow.com/a/38140932 9 | template 10 | inline void hash_combine(std::size_t& seed, std::size_t v, const Rest... rest) 11 | { 12 | seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2); 13 | (hash_combine(seed, rest), ...); 14 | } 15 | 16 | inline void hash_combine(std::size_t& seed, std::vector vs) 17 | { 18 | for (const auto& v : vs) { 19 | hash_combine(seed, v); 20 | } 21 | } 22 | 23 | struct RenderPipelineKey { 24 | const Shader* shader; 25 | WGPUColorTargetState color_target; 26 | RenderPipelineDescription description; 27 | WGPUPipelineLayout pipeline_layout; 28 | 29 | bool operator==(const RenderPipelineKey& other) const; 30 | }; 31 | 32 | template <> 33 | struct std::hash 34 | { 35 | std::size_t operator()(const RenderPipelineKey& k) const 36 | { 37 | using std::size_t; 38 | using std::hash; 39 | using std::string; 40 | 41 | std::size_t h1 = hash()(k.shader); 42 | std::size_t h2 = hash()(static_cast(k.color_target.format)); 43 | std::size_t h3 = hash()(static_cast(k.color_target.writeMask)); 44 | std::size_t h4 = hash()(k.color_target.blend); 45 | std::size_t h5 = hash()(static_cast(k.description.cull_mode)); 46 | std::size_t h6 = hash()(static_cast(k.description.topology)); 47 | std::size_t h7 = hash()(static_cast(k.description.depth_read)); 48 | std::size_t h8 = hash()(static_cast(k.description.depth_write)); 49 | std::size_t h9 = hash()(static_cast(k.description.blending_enabled)); 50 | std::size_t h10 = hash()(static_cast(k.description.topology)); 51 | std::size_t h11 = hash()(static_cast(k.description.depth_compare)); 52 | std::size_t h12 = hash()(k.pipeline_layout); 53 | 54 | std::size_t seed = 0; 55 | hash_combine(seed, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11, h12); 56 | return seed; 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /data/shaders/gaussian_splatting/gs_render.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct CameraData { 3 | view_projection : mat4x4f, 4 | view : mat4x4f, 5 | projection : mat4x4f, 6 | eye : vec3f, 7 | exposure : f32, 8 | right_controller_position : vec3f, 9 | ibl_intensity : f32, 10 | screen_size : vec2f, 11 | dummy : vec2f, 12 | }; 13 | 14 | @group(0) @binding(0) var model : mat4x4f; 15 | @group(0) @binding(1) var centroid : array>; 16 | @group(0) @binding(2) var color : array>; 17 | @group(0) @binding(3) var basis : array>; 18 | 19 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 20 | 21 | struct VertexInput { 22 | @location(0) position: vec2, 23 | #unique instance @location(1) id: u32 24 | }; 25 | 26 | struct VertexOutput { 27 | @builtin(position) clip_position: vec4, 28 | @location(0) coord: vec2, 29 | @location(1) color: vec4 30 | }; 31 | 32 | @vertex 33 | fn vs_main(in: VertexInput) -> VertexOutput 34 | { 35 | var out: VertexOutput; 36 | 37 | var clip_center : vec4 = camera_data.projection * camera_data.view * model * vec4(centroid[in.id].xyz, 1.0); 38 | var ndc_center : vec3 = clip_center.xyz / clip_center.w; 39 | var basis_viewport : vec2 = vec2(2.0 / camera_data.screen_size.x, 2.0 / camera_data.screen_size.y); 40 | var ndc_offset : vec2 = vec2(in.position.x * basis[in.id].xy + in.position.y * basis[in.id].zw) * basis_viewport; 41 | 42 | out.coord = in.position; 43 | out.color = color[in.id]; 44 | 45 | out.clip_position = vec4(ndc_center.xy + ndc_offset, ndc_center.z, 1.0); 46 | 47 | return out; 48 | } 49 | 50 | @fragment 51 | fn fs_main(in: VertexOutput) -> @location(0) vec4 52 | { 53 | //var a = 1.0/(0.25 * sqrt(2.0*3.14))*exp(-0.5 * (in.coord.x*in.coord.x+in.coord.y*in.coord.y)/(0.25*0.25)); 54 | var vPosition = in.coord * 2.0; 55 | 56 | var A = -dot(vPosition, vPosition); 57 | if (A < -4.0) { 58 | discard; 59 | } 60 | 61 | A = exp(A) * in.color.a; 62 | 63 | // gl_FragColor = vec4(color.rgb, A); 64 | var a = 1.0 / (0.25 * sqrt(2.0*3.14)) * exp(-0.5 * (in.coord.x*in.coord.x+in.coord.y*in.coord.y)/(0.25*0.25)); 65 | return vec4(in.color.rgb * camera_data.exposure, a * in.color.a); 66 | } -------------------------------------------------------------------------------- /data/shaders/gaussian_splatting/gs_covariance.wgsl: -------------------------------------------------------------------------------- 1 | @group(0) @binding(0) var rotation :array>; 2 | @group(0) @binding(1) var scaling :array>; 3 | @group(0) @binding(2) var covariance: array>; 4 | @group(0) @binding(6) var splat_count: u32; 5 | 6 | fn quat2mat(quat: vec4) -> mat3x3 7 | { 8 | let x2 = quat.x + quat.x; 9 | let y2 = quat.y + quat.y; 10 | let z2 = quat.z + quat.z; 11 | let xx = quat.x * x2; 12 | let yx = quat.y * x2; 13 | let yy = quat.y * y2; 14 | let zx = quat.z * x2; 15 | let zy = quat.z * y2; 16 | let zz = quat.z * z2; 17 | let wx = quat.w * x2; 18 | let wy = quat.w * y2; 19 | let wz = quat.w * z2; 20 | var out:mat3x3; 21 | out[0][0] = 1 - yy - zz; 22 | out[0][1] = yx + wz; 23 | out[0][2] = zx - wy; 24 | 25 | out[1][0] = yx - wz; 26 | out[1][1] = 1.0 - xx - zz; 27 | out[1][2] = zy + wx; 28 | 29 | out[2][0] = zx + wy; 30 | out[2][1] = zy - wx; 31 | out[2][2] = 1.0 - xx - yy; 32 | 33 | return out; 34 | } 35 | 36 | fn scaling2mat(scaling:vec3) ->mat3x3 37 | { 38 | var out:mat3x3; 39 | out[0][0] = scaling.x; 40 | out[0][1] = 0.0; 41 | out[0][2] = 0.0; 42 | 43 | out[1][0] = 0.0; 44 | out[1][1] = scaling.y; 45 | out[1][2] = 0.0; 46 | 47 | out[2][0] = 0.0; 48 | out[2][1] = 0.0; 49 | out[2][2] = scaling.z; 50 | 51 | return out; 52 | } 53 | 54 | @compute @workgroup_size(256) 55 | fn compute(@builtin(global_invocation_id) GlobalInvocationID : vec3, 56 | @builtin(local_invocation_id) LocalInvocationID: vec3, 57 | @builtin(workgroup_id) WorkgroupID: vec3) 58 | { 59 | if (GlobalInvocationID.x < splat_count) { 60 | 61 | var rotationMatrix: mat3x3 = quat2mat(rotation[GlobalInvocationID.x]); 62 | var scalingMatrix: mat3x3 = scaling2mat(scaling[GlobalInvocationID.x].xyz); 63 | var T: mat3x3 = rotationMatrix * scalingMatrix; 64 | var T_t: mat3x3 = transpose(T); 65 | 66 | var covarianceMatrix = T * T_t; 67 | 68 | covariance[GlobalInvocationID.x * 2] = vec3(covarianceMatrix[0][0], covarianceMatrix[1][0], covarianceMatrix[2][0]); 69 | covariance[GlobalInvocationID.x * 2 + 1] = vec3(covarianceMatrix[1][1], covarianceMatrix[2][1], covarianceMatrix[2][2]); 70 | } 71 | } -------------------------------------------------------------------------------- /src/framework/animation/pose.cpp: -------------------------------------------------------------------------------- 1 | #include "pose.h" 2 | 3 | Pose::Pose() 4 | { 5 | 6 | } 7 | 8 | Pose::Pose(size_t numJoints) 9 | { 10 | resize(numJoints); 11 | } 12 | 13 | Pose::Pose(const Pose& p) 14 | { 15 | *this = p; 16 | } 17 | 18 | // resize arrays 19 | void Pose::resize(size_t size) 20 | { 21 | parents.resize(size); 22 | joints.resize(size); 23 | } 24 | 25 | // get the number of joints 26 | size_t Pose::size() const 27 | { 28 | return joints.size(); 29 | } 30 | 31 | // set parent id 32 | void Pose::set_parent(size_t id, int parent_id) 33 | { 34 | assert(id < parents.size()); 35 | parents[id] = parent_id; 36 | } 37 | 38 | // get parent id 39 | int Pose::get_parent(size_t id) 40 | { 41 | return parents[id]; 42 | } 43 | 44 | //set local transform of the joint 45 | void Pose::set_local_transform(size_t id, const Transform& transform) 46 | { 47 | assert(id < joints.size()); 48 | joints[id] = transform; 49 | } 50 | 51 | // get local transform of the joint 52 | Transform& Pose::get_local_transform(size_t id) 53 | { 54 | return joints[id]; 55 | } 56 | 57 | // get global (world) transform of the joint 58 | Transform Pose::get_global_transform(size_t id) 59 | { 60 | if (id >= joints.size()) { 61 | assert(0); 62 | } 63 | 64 | Transform transform = joints[id]; 65 | 66 | int parent_id = get_parent(id); 67 | if (parent_id >= 0) { 68 | transform = Transform::combine(get_global_transform(parent_id), transform); 69 | } 70 | 71 | return transform; 72 | } 73 | 74 | Transform Pose::operator[](size_t index) 75 | { 76 | return get_global_transform(index); 77 | } 78 | 79 | Pose& Pose::operator=(const Pose& other) 80 | { 81 | if (this == &other) { 82 | return *this; 83 | } 84 | joints = other.joints; 85 | parents = other.parents; 86 | return *this; 87 | } 88 | 89 | // get global matrices of the joints 90 | std::vector Pose::get_global_matrices() 91 | { 92 | uint32_t joints_count = (uint32_t)size(); 93 | std::vector out(joints_count); 94 | if (out.size() != joints_count) { 95 | out.resize(joints_count); 96 | } 97 | 98 | for (uint32_t i = 0; i < joints_count; i++) { 99 | Transform t = get_global_transform(i); 100 | out[i] = Transform::transform_to_mat4(t); 101 | } 102 | 103 | return out; 104 | } 105 | -------------------------------------------------------------------------------- /docs/reference/mesh.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/graphics/pipeline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "webgpu_context.h" 4 | 5 | #include 6 | #include 7 | 8 | class Shader; 9 | class Mesh; 10 | 11 | using WGPUPipeline = std::variant; 12 | 13 | class Pipeline { 14 | 15 | public: 16 | 17 | ~Pipeline(); 18 | 19 | static WebGPUContext* webgpu_context; 20 | 21 | void create_render(Shader* shader, const WGPUColorTargetState& p_color_target, const RenderPipelineDescription& desc = {}, const std::vector& constants = {}); 22 | void create_render_async(Shader* shader, const WGPUColorTargetState& p_color_target, const RenderPipelineDescription& desc = {}, const std::vector& constants = {}); 23 | 24 | //void create_compute(Shader* shader, WGPUPipelineLayout pipeline_layout); 25 | void create_compute(Shader* shader, const std::string& entry_point = "compute", const std::vector& constants = {}); 26 | 27 | //void create_compute_async(Shader* shader, WGPUPipelineLayout pipeline_layout); 28 | void create_compute_async(Shader* shader, const std::string& entry_point = "compute", const std::vector& constants = {}); 29 | 30 | void reload(Shader* shader); 31 | 32 | bool set(const WGPURenderPassEncoder& render_pass) const; 33 | bool set(const WGPUComputePassEncoder& compute_pass) const; 34 | 35 | bool is_render_pipeline() const; 36 | bool is_compute_pipeline() const; 37 | 38 | bool is_msaa_allowed() const; 39 | 40 | inline bool is_loaded() const { 41 | return loaded; 42 | } 43 | 44 | friend void render_pipeline_creation_callback(WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline pipeline, struct WGPUStringView message, void* userdata1, void* userdata2); 45 | friend void compute_pipeline_creation_callback(WGPUCreatePipelineAsyncStatus status, WGPUComputePipeline pipeline, struct WGPUStringView message, void* userdata1, void* userdata2); 46 | 47 | private: 48 | 49 | void create_render_common(Shader* shader, const WGPUColorTargetState& p_color_target, const RenderPipelineDescription& desc = {}); 50 | void create_compute_common(Shader* shader); 51 | 52 | WGPUPipeline pipeline; 53 | 54 | WGPUColorTargetState color_target; 55 | WGPUBlendState const* blend_state = nullptr; 56 | RenderPipelineDescription description; 57 | 58 | bool async_compile = false; 59 | bool loaded = false; 60 | }; 61 | -------------------------------------------------------------------------------- /src/framework/animation/track.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "interpolator.h" 4 | 5 | #include "glm/vec2.hpp" 6 | 7 | #include "framework/nodes/node.h" 8 | 9 | enum eTrackType { 10 | TYPE_UNDEFINED, 11 | TYPE_FLOAT, // Set a value in a property, can be interpolated. 12 | TYPE_VECTOR2, // Vector3 track, can be compressed. 13 | TYPE_VECTOR3, // Vector3 track, can be compressed. 14 | TYPE_VECTOR4, 15 | TYPE_QUAT, // Quaternion track, can be compressed. 16 | TYPE_METHOD, // Call any method on a specific node. 17 | TYPE_POSITION, 18 | TYPE_ROTATION, 19 | TYPE_SCALE 20 | }; 21 | 22 | enum eLoopType : uint8_t { 23 | ANIMATION_LOOP_NONE, 24 | ANIMATION_LOOP_DEFAULT, 25 | ANIMATION_LOOP_REVERSE, 26 | ANIMATION_LOOP_PING_PONG 27 | }; 28 | 29 | // Collection of keyframes 30 | class Track { 31 | 32 | int id = 0; 33 | std::vector keyframes; 34 | eTrackType type = eTrackType::TYPE_UNDEFINED; 35 | std::string name = ""; 36 | std::string path = ""; 37 | 38 | Interpolator interpolator; 39 | 40 | int frame_index(float time); 41 | 42 | // Takes an input time that is outside the range of the track and adjusts it to be a valid time on the track 43 | float adjust_time_to_fit_track(float time, bool loop); 44 | 45 | public: 46 | 47 | Track(); 48 | 49 | int get_id() const; 50 | eTrackType get_type() const { return type; }; 51 | const std::string& get_name() const; 52 | const std::string& get_path() const; 53 | Interpolator& get_interpolator() { return interpolator; } 54 | 55 | void set_id(int id); 56 | void set_type(eTrackType new_type) { type = new_type; }; 57 | void set_name(const std::string& new_name); 58 | void set_path(const std::string& new_path); 59 | 60 | size_t size(); 61 | void resize(size_t size); 62 | 63 | float get_end_time() const; 64 | float get_start_time() const; 65 | Keyframe& get_keyframe(uint32_t index); 66 | uint32_t get_keyframe_index(float time); 67 | uint32_t add_keyframe(const Keyframe& k, bool sort = false); 68 | void delete_keyframe(int keyframe_idx); 69 | 70 | void serialize(std::ofstream& binary_scene_file); 71 | void parse(std::ifstream& binary_scene_file); 72 | 73 | // Prameters: time value, if the track is looping or not 74 | TrackType sample(float time, bool looping, Node::AnimatableProperty* out = nullptr, eInterpolationType interpolation_type = INTERPOLATION_UNSET); 75 | Keyframe& operator[](uint32_t index); 76 | }; 77 | -------------------------------------------------------------------------------- /src/framework/nodes/light_3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | 5 | #include "node_3d.h" 6 | 7 | #include "graphics/uniforms_structs.h" 8 | #include "framework/camera/camera.h" 9 | 10 | enum LightType { 11 | LIGHT_UNDEFINED, 12 | LIGHT_DIRECTIONAL, 13 | LIGHT_OMNI, 14 | LIGHT_SPOT 15 | }; 16 | 17 | class Light3D : public Node3D { 18 | 19 | protected: 20 | 21 | LightType type = LIGHT_UNDEFINED; 22 | 23 | float intensity = 1.0f; 24 | glm::vec3 color = { 1.0f, 1.0f, 1.0f }; 25 | float range = 5.0f; 26 | 27 | // Fading 28 | 29 | bool fading_enabled = false; 30 | float fade_begin = 10.0f; 31 | float fade_length = 1.0f; 32 | 33 | // Shadows 34 | Camera light_camera; 35 | bool cast_shadows = false; 36 | float shadow_bias = 0.0001f; 37 | 38 | WGPUTexture shadow_depth_texture = nullptr; 39 | WGPUTextureView shadow_depth_texture_view = nullptr; 40 | 41 | public: 42 | 43 | Light3D(); 44 | ~Light3D(); 45 | 46 | virtual void render() override; 47 | 48 | void clone(Node* new_node, bool copy = true) override; 49 | 50 | void render_gui() override; 51 | 52 | virtual void get_uniform_data(sLightUniformData& data); 53 | LightType get_type() const { return type; } 54 | float get_intensity() const { return intensity; } 55 | const glm::vec3& get_color() { return color; } 56 | glm::vec3 get_color() const { return color; } 57 | bool get_fading_enabled() const { return fading_enabled; } 58 | bool get_cast_shadows() const { return cast_shadows; } 59 | float get_range() const { return range; } 60 | float get_shadow_bias() const { return shadow_bias; } 61 | 62 | virtual void create_debug_meshes() = 0; 63 | 64 | virtual void set_color(const glm::vec3& color); 65 | void set_intensity(float value); 66 | virtual void set_range(float value); 67 | void set_fading_enabled(bool value); 68 | void set_cast_shadows(bool value); 69 | void set_shadow_bias(float new_shadow_bias); 70 | 71 | void create_shadow_data(); 72 | 73 | void on_set_color(); 74 | virtual void on_set_range() {}; 75 | 76 | const Camera& get_light_camera() { return light_camera; } 77 | WGPUTexture get_shadow_depth_texture() { return shadow_depth_texture; } 78 | WGPUTextureView get_shadow_depth_texture_view() { return shadow_depth_texture_view; } 79 | 80 | void serialize(std::ofstream& binary_scene_file) override; 81 | void parse(std::ifstream& binary_scene_file) override; 82 | }; 83 | -------------------------------------------------------------------------------- /docs/reference/pipeline.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/framework/parsers/parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/nodes/node.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | enum eParseFlags { 12 | PARSE_NO_FLAGS = 0, 13 | PARSE_GLTF_CLEAR_CACHE = 1 << 0, 14 | PARSE_GLTF_FILL_SURFACE_DATA = 1 << 1, 15 | PARSE_DEFAULT = PARSE_GLTF_CLEAR_CACHE 16 | }; 17 | 18 | class Parser { 19 | 20 | protected: 21 | std::future async_future; 22 | std::function&, bool)> async_callback; 23 | std::vector async_nodes; 24 | 25 | static std::vector async_parsers; 26 | 27 | virtual void on_async_finished() {}; 28 | 29 | public: 30 | virtual bool parse(std::string file_path, std::vector& entities, uint32_t flags = PARSE_DEFAULT) { return false; }; 31 | virtual bool read_data(int8_t* byte_array, uint32_t array_size, Node* scene_root, std::vector& entities, uint32_t flags = PARSE_DEFAULT) { return false; }; 32 | 33 | template 34 | static void parse_async(const std::string& file_path, std::function&, bool)> callback, uint32_t flags = PARSE_DEFAULT); 35 | 36 | template 37 | static void read_data_async(int8_t* byte_array, uint32_t array_size, Node* scene_root, std::function&, bool)> callback, uint32_t flags = PARSE_DEFAULT); 38 | 39 | static void poll_async_parsers(); 40 | }; 41 | 42 | template 43 | void Parser::parse_async(const std::string& file_path, std::function&, bool)> callback, uint32_t flags) 44 | { 45 | Parser* async_parser = new T(); 46 | 47 | async_parser->async_callback = callback; 48 | 49 | // Start the async operation 50 | async_parser->async_future = std::async(std::launch::async, &Parser::parse, async_parser, std::move(file_path), std::ref(async_parser->async_nodes), flags); 51 | 52 | async_parsers.push_back(async_parser); 53 | } 54 | 55 | template 56 | void Parser::read_data_async(int8_t* byte_array, uint32_t array_size, Node* scene_root, std::function&, bool)> callback, uint32_t flags) 57 | { 58 | Parser* async_parser = new T(); 59 | 60 | async_parser->async_callback = callback; 61 | 62 | // Start the async operation 63 | async_parser->async_future = std::async(std::launch::async, &Parser::read_data, async_parser, byte_array, array_size, scene_root, std::ref(async_parser->async_nodes), flags); 64 | 65 | async_parsers.push_back(async_parser); 66 | } 67 | -------------------------------------------------------------------------------- /src/graphics/primitives/capsule_mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "capsule_mesh.h" 2 | 3 | #include "framework/math/math_utils.h" 4 | #include "framework/colors.h" 5 | 6 | #include "graphics/renderer_storage.h" 7 | 8 | #include "spdlog/spdlog.h" 9 | #include "imgui.h" 10 | 11 | CapsuleMesh::CapsuleMesh(float radius, float height, uint32_t rings, uint32_t ring_segments, const glm::vec3& color) 12 | : PrimitiveMesh(color), radius(radius), height(height), rings(rings), ring_segments(ring_segments) 13 | { 14 | mesh_type = "CapsuleMesh"; 15 | 16 | build_mesh(); 17 | } 18 | 19 | void CapsuleMesh::build_mesh() 20 | { 21 | surface->create_capsule(radius, height, rings, ring_segments, color); 22 | } 23 | 24 | void CapsuleMesh::set_radius(float new_radius) 25 | { 26 | radius = new_radius; 27 | build_mesh(); 28 | } 29 | 30 | void CapsuleMesh::set_height(float new_height) 31 | { 32 | height = new_height; 33 | build_mesh(); 34 | } 35 | 36 | void CapsuleMesh::set_rings(uint32_t new_rings) 37 | { 38 | rings = new_rings; 39 | build_mesh(); 40 | } 41 | 42 | void CapsuleMesh::set_ring_segments(uint32_t new_ring_segments) 43 | { 44 | ring_segments = new_ring_segments; 45 | build_mesh(); 46 | } 47 | 48 | void CapsuleMesh::render_gui() 49 | { 50 | ImGui::Text("Radius"); 51 | ImGui::SameLine(200); 52 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 53 | if (ImGui::DragFloat("##Radius", &radius, 0.01f, 0.01f, 2.0f)) { 54 | dirty = true; 55 | } 56 | 57 | ImGui::Text("Height"); 58 | ImGui::SameLine(200); 59 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 60 | if (ImGui::DragFloat("##Height", &height, 0.01f, 0.01f, 2.0f)) { 61 | dirty = true; 62 | } 63 | 64 | ImGui::Text("Rings"); 65 | ImGui::SameLine(200); 66 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 67 | int rings_int = static_cast(rings); 68 | if (ImGui::DragInt("##Rings", &rings_int, 1, 3, 64)) { 69 | rings = static_cast(rings_int); 70 | dirty = true; 71 | } 72 | 73 | ImGui::Text("Ring Segments"); 74 | ImGui::SameLine(200); 75 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 76 | int ring_seg_int = static_cast(ring_segments); 77 | if (ImGui::DragInt("##Ring Segments", &ring_seg_int, 1, 3, 64)) { 78 | ring_segments = static_cast(ring_seg_int); 79 | dirty = true; 80 | } 81 | 82 | if (dirty) { 83 | build_mesh(); 84 | dirty = false; 85 | } 86 | 87 | PrimitiveMesh::render_gui(); 88 | } 89 | -------------------------------------------------------------------------------- /src/xr/webxr/webxr_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "xr/xr_context.h" 4 | 5 | #include "includes.h" 6 | 7 | #ifdef WEBXR_SUPPORT 8 | 9 | #include "webxr.h" 10 | 11 | // WEBXR_BUTTON_THUMBSTICK, 12 | enum WEBXR_BUTTONS 13 | { 14 | WEBXR_BUTTON_TRIGGER = 0, 15 | WEBXR_BUTTON_GRAB, 16 | WEBXR_BUTTON_TOUCHPAD, 17 | WEBXR_BUTTON_THUMBSTICK_PRESS, 18 | WEBXR_BUTTON_AX, 19 | WEBXR_BUTTON_BY, 20 | WEBXR_BUTTON_COUNT 21 | }; 22 | 23 | struct WebXRContext : public XRContext { 24 | 25 | virtual ~WebXRContext(); 26 | 27 | /* 28 | * XR General 29 | */ 30 | 31 | bool session_queried = false; 32 | bool session_supported = false; 33 | 34 | bool init(WebGPUContext* webgpu_context) override; 35 | void clean() override; 36 | 37 | /* 38 | * XR Input 39 | */ 40 | 41 | // All buttons, Trigger and Grip 42 | std::vector handButtons[HAND_COUNT]; 43 | std::vector buttonsState; 44 | glm::vec2 axisState[HAND_COUNT]; 45 | 46 | //void init_actions(); 47 | void poll_actions() override; 48 | 49 | //void apply_haptics(uint8_t controller, float amplitude, float duration); 50 | //void stop_haptics(uint8_t controller); 51 | 52 | /* 53 | * XR Session 54 | */ 55 | 56 | bool query_session_supported(); 57 | bool is_session_supported() const { return session_supported; } 58 | void set_session_supported(bool value); 59 | void on_frame(WebXRRigidTransform* head_pose, WebXRView views[2], WGPUTextureView texture_view_left, WGPUTextureView texture_view_right); 60 | bool begin_session() override; 61 | bool end_session() override; 62 | 63 | /* 64 | * Render 65 | */ 66 | 67 | WGPUTextureView swapchain_views[2] = {}; 68 | 69 | // inverted for reverse-z 70 | float z_near = 1000.0f; 71 | float z_far = 0.01f; 72 | 73 | void update_views(WebXRRigidTransform* head_pose, WebXRView views[2], WGPUTextureView texture_view_left, WGPUTextureView texture_view_right); 74 | 75 | WGPUTextureView get_swapchain_view(uint8_t eye_idx, uint32_t image_idx) override; 76 | WGPUTextureView get_swapchain_view(uint8_t eye_idx) override; 77 | 78 | void update() override; 79 | 80 | /* 81 | * Debug & Errors 82 | */ 83 | 84 | void print_viewconfig_view_info() override; 85 | 86 | void print_reference_spaces() override; 87 | 88 | void print_error(int error); 89 | 90 | private: 91 | 92 | }; 93 | 94 | #else 95 | 96 | struct WebXRContext { 97 | 98 | }; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/graphics/primitives/torus_mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "torus_mesh.h" 2 | 3 | #include "framework/math/math_utils.h" 4 | #include "framework/colors.h" 5 | 6 | #include "graphics/renderer_storage.h" 7 | 8 | #include "spdlog/spdlog.h" 9 | #include "imgui.h" 10 | 11 | TorusMesh::TorusMesh(float ring_radius, float tube_radius, uint32_t rings, uint32_t ring_segments, const glm::vec3& color) 12 | : PrimitiveMesh(color), ring_radius(ring_radius), tube_radius(tube_radius), rings(rings), ring_segments(ring_segments) 13 | { 14 | mesh_type = "TorusMesh"; 15 | 16 | build_mesh(); 17 | } 18 | 19 | void TorusMesh::build_mesh() 20 | { 21 | surface->create_torus(ring_radius, tube_radius, rings, ring_segments, color); 22 | } 23 | 24 | void TorusMesh::set_ring_radius(float new_ring_radius) 25 | { 26 | ring_radius = new_ring_radius; 27 | build_mesh(); 28 | } 29 | 30 | void TorusMesh::set_tube_radius(float new_tube_radius) 31 | { 32 | tube_radius = new_tube_radius; 33 | build_mesh(); 34 | } 35 | 36 | void TorusMesh::set_rings(uint32_t new_rings) 37 | { 38 | rings = new_rings; 39 | build_mesh(); 40 | } 41 | 42 | void TorusMesh::set_ring_segments(uint32_t new_ring_segments) 43 | { 44 | ring_segments = new_ring_segments; 45 | build_mesh(); 46 | } 47 | 48 | void TorusMesh::render_gui() 49 | { 50 | ImGui::Text("Ring radius"); 51 | ImGui::SameLine(200); 52 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 53 | if (ImGui::DragFloat("##Ring radius", &ring_radius, 0.01f, 0.01f, 2.0f)) { 54 | dirty = true; 55 | } 56 | 57 | ImGui::Text("Tube radius"); 58 | ImGui::SameLine(200); 59 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 60 | if (ImGui::DragFloat("##Tube radius", &tube_radius, 0.01f, 0.01f, 2.0f)) { 61 | dirty = true; 62 | } 63 | 64 | ImGui::Text("Rings"); 65 | ImGui::SameLine(200); 66 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 67 | int rings_int = static_cast(rings); 68 | if (ImGui::DragInt("##Rings", &rings_int, 1, 3, 64)) { 69 | rings = static_cast(rings_int); 70 | dirty = true; 71 | } 72 | 73 | ImGui::Text("Ring Segments"); 74 | ImGui::SameLine(200); 75 | ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); 76 | int ring_seg_int = static_cast(ring_segments); 77 | if (ImGui::DragInt("##Ring Segments", &ring_seg_int, 1, 3, 64)) { 78 | ring_segments = static_cast(ring_seg_int); 79 | dirty = true; 80 | } 81 | 82 | if (dirty) { 83 | build_mesh(); 84 | dirty = false; 85 | } 86 | 87 | PrimitiveMesh::render_gui(); 88 | } 89 | -------------------------------------------------------------------------------- /docs/materials.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/framework/nodes/gs_node.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "includes.h" 4 | 5 | #include "graphics/uniform.h" 6 | #include "graphics/pipeline.h" 7 | #include "graphics/kernels/radix_sort_kernel.h" 8 | 9 | #include "framework/nodes/node_3d.h" 10 | 11 | class Shader; 12 | 13 | struct sGSRenderData { 14 | glm::vec2 position; 15 | uint32_t id; 16 | }; 17 | 18 | class GSNode : public Node3D { 19 | 20 | uint32_t splat_count = 0; 21 | uint32_t padded_splat_count = 0; 22 | uint32_t workgroup_size = 0; 23 | 24 | WGPUBuffer render_buffer = nullptr; 25 | WGPUBuffer ids_buffer = nullptr; 26 | 27 | // Render 28 | Uniform model_uniform; 29 | Uniform centroid_uniform; 30 | Uniform basis_uniform; 31 | Uniform color_uniform; 32 | WGPUBindGroup render_bindgroup = nullptr; 33 | 34 | // Covariance 35 | Uniform rotations_uniform; 36 | Uniform scales_uniform; 37 | Uniform covariance_uniform; 38 | Uniform splat_count_uniform; 39 | WGPUBindGroup covariance_bindgroup = nullptr; 40 | 41 | // Basis 42 | WGPUBuffer distances_buffer = nullptr; 43 | 44 | Uniform basis_distances_uniform; 45 | Uniform ids_basis_uniform; 46 | WGPUBindGroup basis_uniform_bindgroup = nullptr; 47 | 48 | RadixSortKernel* radix_sort_kernel = nullptr; 49 | 50 | Shader* covariance_shader = nullptr; 51 | Pipeline covariance_pipeline; 52 | 53 | Shader* basis_shader = nullptr; 54 | Pipeline basis_pipeline; 55 | 56 | bool covariance_calculated = false; 57 | 58 | public: 59 | 60 | bool is_skinned = false; 61 | 62 | GSNode(); 63 | ~GSNode(); 64 | 65 | void initialize(uint32_t splat_count); 66 | 67 | virtual void render() override; 68 | virtual void update(float delta_time) override; 69 | 70 | void sort(WGPUComputePassEncoder compute_pass); 71 | 72 | WGPUBuffer get_render_buffer() { 73 | return render_buffer; 74 | } 75 | 76 | WGPUBuffer get_ids_buffer() { 77 | return ids_buffer; 78 | } 79 | 80 | WGPUBindGroup get_render_bindgroup() { 81 | return render_bindgroup; 82 | } 83 | 84 | bool is_covariance_calculated() { return covariance_calculated; } 85 | 86 | void set_render_buffers(const std::vector& positions, const std::vector& colors); 87 | void set_covariance_buffers(const std::vector& rotations, const std::vector& scales); 88 | 89 | void calculate_basis(WGPUComputePassEncoder compute_pass); 90 | void calculate_covariance(WGPUComputePassEncoder compute_pass); 91 | 92 | uint32_t get_splat_count(); 93 | uint32_t get_padded_splat_count(); 94 | uint32_t get_splats_render_bytes_size(); 95 | uint32_t get_ids_render_bytes_size(); 96 | 97 | }; 98 | -------------------------------------------------------------------------------- /data/shaders/mesh_grid.wgsl: -------------------------------------------------------------------------------- 1 | #include mesh_includes.wgsl 2 | 3 | @group(0) @binding(0) var mesh_data : InstanceData; 4 | 5 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 6 | 7 | @group(2) @binding(1) var albedo: vec4f; 8 | 9 | @vertex 10 | fn vs_main(in: VertexInput) -> VertexOutput { 11 | 12 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 13 | 14 | var out: VertexOutput; 15 | var world_position = instance_data.model * vec4f(in.position, 1.0); 16 | out.world_position = world_position.xyz; 17 | out.position = camera_data.view_projection * world_position; 18 | out.uv = in.uv; // forward to the fragment shader 19 | out.color = vec4(in.color, 1.0) * albedo; 20 | out.normal = in.normal; 21 | return out; 22 | } 23 | 24 | struct FragmentOutput { 25 | @location(0) color: vec4f 26 | } 27 | 28 | // https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8#5ef5 29 | fn pristine_grid(uv : vec2f, lineWidth : vec2f) -> f32 30 | { 31 | let ddx : vec2f = dpdx(uv); 32 | let ddy : vec2f = dpdy(uv); 33 | let uvDeriv : vec2f = vec2f(length(vec2f(ddx.x, ddy.x)), length(vec2(ddx.y, ddy.y))); 34 | let invertLine : vec2 = vec2(lineWidth.x > 0.5, lineWidth.y > 0.5); 35 | let targetWidth : vec2f = vec2f( 36 | select(lineWidth.x, 1.0 - lineWidth.x, invertLine.x), 37 | select(lineWidth.y, 1.0 - lineWidth.y, invertLine.y)); 38 | 39 | let drawWidth : vec2f = clamp(targetWidth, uvDeriv, vec2f(0.5)); 40 | let lineAA : vec2f = uvDeriv * 1.5; 41 | var gridUV : vec2f = abs(fract(uv) * 2.0 - 1.0); 42 | gridUV.x = select(1.0 - gridUV.x, gridUV.x, invertLine.x); 43 | gridUV.y = select(1.0 - gridUV.y, gridUV.y, invertLine.y); 44 | var grid2 : vec2f = smoothstep(drawWidth + lineAA, drawWidth - lineAA, gridUV); 45 | 46 | grid2 *= clamp(targetWidth / drawWidth, vec2f(0.0), vec2f(1.0)); 47 | grid2 = mix(grid2, targetWidth, clamp(uvDeriv * 2.0 - 1.0, vec2f(0.0), vec2f(1.0))); 48 | grid2.x = select(grid2.x, 1.0 - grid2.x, invertLine.x); 49 | grid2.y = select(grid2.y, 1.0 - grid2.y, invertLine.y); 50 | 51 | return mix(grid2.x, 1.0, grid2.y); 52 | } 53 | 54 | const GRID_AREA_SIZE : f32 = 25.0; 55 | const GRID_QUAD_SIZE :f32 = 1.0; 56 | const LINE_WIDTH : f32 = 0.015; 57 | 58 | @fragment 59 | fn fs_main(in: VertexOutput) -> FragmentOutput { 60 | 61 | var dummy = camera_data.eye; 62 | 63 | let wrapped_uvs : vec2f = (in.uv * GRID_AREA_SIZE / GRID_QUAD_SIZE); 64 | 65 | var out: FragmentOutput; 66 | 67 | out.color = vec4f(0.27, 0.27, 0.27, 1.0) * 68 | pristine_grid(wrapped_uvs, vec2f(LINE_WIDTH)); 69 | 70 | out.color.a *= (1.0 - 2.0 * distance(vec2f(0.5), in.uv.xy)); 71 | 72 | return out; 73 | } -------------------------------------------------------------------------------- /src/engine/engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/camera/camera.h" 4 | 5 | struct GLFWwindow; 6 | class FileWatcher; 7 | class Renderer; 8 | class Scene; 9 | class Node; 10 | 11 | struct sEngineConfiguration { 12 | uint16_t window_width = 1600; 13 | uint16_t window_height = 900; 14 | std::string window_title = "wgpuEngine"; 15 | eCameraType camera_type = CAMERA_FLYOVER; 16 | glm::vec3 camera_eye = { 0.0f, 0.75f, 2.0f }; 17 | glm::vec3 camera_center = { 0.0f, 0.75f, 0.0f }; 18 | uint8_t msaa_count = 1; 19 | bool fullscreen = false; 20 | }; 21 | 22 | class Engine { 23 | 24 | void init_imgui(GLFWwindow* window); 25 | void init_shader_watchers(); 26 | 27 | protected: 28 | 29 | Renderer* renderer = nullptr; 30 | FileWatcher* shader_reload_watcher = nullptr; 31 | FileWatcher* engine_shader_reload_watcher = nullptr; 32 | Scene* main_scene = nullptr; 33 | 34 | sEngineConfiguration configuration = {}; 35 | 36 | Node* selected_node = nullptr; 37 | 38 | bool use_glfw = false; 39 | float delta_time = 0.0f; 40 | double current_time = 0.0; 41 | 42 | bool stop_game_loop = false; 43 | 44 | bool show_imgui = true; 45 | 46 | #ifdef __EMSCRIPTEN__ 47 | bool wasm_module_initialized = false; 48 | #endif 49 | 50 | bool pre_initialize_renderer(); 51 | 52 | bool initialize_step(); 53 | 54 | public: 55 | 56 | static Engine* instance; 57 | 58 | Engine(); 59 | ~Engine(); 60 | 61 | virtual int initialize(Renderer* renderer, const sEngineConfiguration& configuration = {}); 62 | virtual int post_initialize(); 63 | virtual void clean(); 64 | 65 | virtual void start_loop(); 66 | virtual void on_frame(); 67 | virtual void update(float delta_time); 68 | virtual void render(); 69 | 70 | virtual void render_default_gui(); 71 | bool render_scene_tree_recursive(Node* entity); 72 | void add_node(Node* node); 73 | virtual void resize_window(int width, int height); 74 | void vibrate_hand(int controller, float amplitude, float duration); 75 | 76 | static Engine* get_instance() { return instance; } 77 | sEngineConfiguration get_configuration() { return configuration; } 78 | Renderer* get_renderer() { return renderer; } 79 | bool get_xr_available(); 80 | bool get_use_mirror_window(); 81 | bool should_close(); 82 | Scene* get_main_scene(); 83 | float get_delta_time() { return delta_time; } 84 | void get_scene_ray(glm::vec3& ray_origin, glm::vec3& ray_direction); 85 | 86 | void set_main_scene(Scene* scene); 87 | virtual void set_main_scene(const std::string& scene_path); 88 | 89 | #ifdef __EMSCRIPTEN__ 90 | bool get_wasm_module_initialized() const { return wasm_module_initialized; } 91 | void set_wasm_module_initialized(bool value) { wasm_module_initialized = value; } 92 | #endif 93 | }; 94 | -------------------------------------------------------------------------------- /src/framework/animation/interpolator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/vec2.hpp" 4 | #include "glm/vec3.hpp" 5 | #include "glm/vec4.hpp" 6 | #include "glm/gtc/quaternion.hpp" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | typedef std::variant TrackType; 13 | 14 | #ifdef __EMSCRIPTEN__ 15 | #include 16 | #include 17 | #include 18 | 19 | TrackType from_val(const emscripten::val& v); 20 | emscripten::val to_val(const TrackType& v); 21 | #endif 22 | 23 | class Keyframe { 24 | public: 25 | TrackType value;//value 26 | TrackType in; //in tangent 27 | TrackType out; //out tangent 28 | float time; //frame time 29 | 30 | #ifdef __EMSCRIPTEN__ 31 | emscripten::val get_value() const { return to_val(value); } 32 | void set_value(emscripten::val v) { value = from_val(v); } 33 | 34 | emscripten::val get_in() const { return to_val(in); } 35 | void set_in(emscripten::val v) { in = from_val(v); } 36 | 37 | emscripten::val get_out() const { return to_val(out); } 38 | void set_out(emscripten::val v) { out = from_val(v); } 39 | #endif 40 | }; 41 | 42 | enum eInterpolationType { 43 | INTERPOLATION_UNSET, 44 | INTERPOLATION_STEP, 45 | INTERPOLATION_LINEAR, 46 | INTERPOLATION_CUBIC 47 | }; 48 | 49 | class Interpolator { 50 | 51 | eInterpolationType type = INTERPOLATION_LINEAR; 52 | 53 | TrackType step(const std::vector& keyframes, int frame_idx, bool looping); 54 | TrackType linear(const std::vector& keyframes, float time, int frame_idx, bool looping); 55 | TrackType cubic(const std::vector& keyframes, float time, int frame_idx, bool looping); 56 | 57 | 58 | /* 59 | * Linear interpolation 60 | */ 61 | 62 | TrackType linear_interpolate(TrackType a, TrackType b, float t); 63 | 64 | /* 65 | * Cubic spline interpolation 66 | */ 67 | 68 | TrackType hermite(float time, float delta_time, const TrackType& p1, const TrackType& s1, const TrackType& p2, const TrackType& s2); 69 | TrackType adjust_hermite_result(const TrackType& r); 70 | void neighborhood(const TrackType& a, TrackType& b); 71 | 72 | template 73 | TrackType cubic_interpolate(const TrackType& p1, const TrackType& s1, const TrackType& p2, const TrackType& s2, float h1, float h2, float h3, float h4); 74 | 75 | public: 76 | 77 | Interpolator(); 78 | Interpolator(eInterpolationType new_type); 79 | 80 | TrackType interpolate(const std::vector& keyframes, float time, int frame_idx, bool looping); 81 | 82 | eInterpolationType get_type() const; 83 | void set_type(eInterpolationType new_type); 84 | }; 85 | -------------------------------------------------------------------------------- /docs/reference/shader.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /glsl_builder.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | import os 4 | 5 | def preprocess_includes(shader_source, shader_directory): 6 | shader_lines = shader_source.splitlines() 7 | 8 | shader_libs = "" 9 | 10 | for line_idx in range(len(shader_lines)): 11 | line_words = shader_lines[line_idx].split() 12 | if len(line_words) > 0 and line_words[0] == "#include": 13 | shader_include_path = line_words[1] 14 | shader_libs += "\"" + shader_include_path + "\", " 15 | shader_include_file = open(shader_directory + shader_include_path, "r") 16 | shader_lines[line_idx] = shader_include_file.read() 17 | shader_include_file.close() 18 | 19 | # remove last ", " 20 | shader_libs = shader_libs[:-2] 21 | 22 | output = [] 23 | for line in shader_lines: 24 | if line: 25 | output.append(",".join(str(ord(c)) for c in line)) 26 | output.append("%s" % ord("\n")) 27 | output.append("0") 28 | return ",".join(output), shader_libs 29 | 30 | 31 | def main(argv): 32 | 33 | if len(sys.argv) == 1: 34 | print("Missing filename parameter") 35 | exit() 36 | 37 | original_shader_path = sys.argv[1] 38 | original_shader_file = open(original_shader_path, "r") 39 | 40 | 41 | shader_directory = os.path.dirname(os.path.realpath(original_shader_path)) 42 | 43 | processed_shader_path = "../../src/shaders/" + original_shader_path + ".gen.h" 44 | 45 | processed_shader_path = Path(processed_shader_path) 46 | processed_shader_path.parent.mkdir(exist_ok=True, parents=True) 47 | 48 | processed_shader_file = open(processed_shader_path, "w") 49 | 50 | preprocessed_shader, shader_libs = preprocess_includes(original_shader_file.read(), shader_directory + "/") 51 | 52 | shader_name = Path(original_shader_path).stem 53 | 54 | generated_content = "//Generated file, do not modify!\n" 55 | generated_content += "#pragma once\n\n" 56 | generated_content += "#include \n" 57 | generated_content += "#include \n\n" 58 | generated_content += "namespace shaders {\n\n" 59 | generated_content += "struct " + shader_name + " {\n\n" 60 | generated_content += "inline static const char path[] = \"" + original_shader_path + "\";\n" 61 | generated_content += "inline static const std::vector libraries = {" + shader_libs + "};\n" 62 | generated_content += "inline static const char source[] = " 63 | 64 | generated_content += "{\n%s\n\t\t};" % preprocessed_shader 65 | 66 | # close struct 67 | generated_content += "\n};\n" 68 | 69 | # close namespace 70 | generated_content += "\n}\n" 71 | 72 | processed_shader_file.write(generated_content) 73 | 74 | processed_shader_file.close() 75 | 76 | original_shader_file.close() 77 | 78 | if __name__ == "__main__": 79 | main(sys.argv[1:]) 80 | -------------------------------------------------------------------------------- /src/framework/nodes/slider_2d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/colors.h" 4 | #include "graphics/mesh.h" 5 | #include "framework/nodes/panel_2d.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace ui { 13 | 14 | enum SliderMode { 15 | HORIZONTAL, 16 | VERTICAL 17 | }; 18 | 19 | struct sSliderDescription { 20 | std::string path = ""; 21 | float fvalue = 0.0f; 22 | int ivalue = 0; 23 | int mode = SliderMode::VERTICAL; 24 | uint32_t flags = 0u; 25 | float fvalue_min = 0.0f; 26 | float fvalue_max = 1.0f; 27 | int precision = 1; 28 | int ivalue_min = 0; 29 | int ivalue_max = 10; 30 | void* p_data = nullptr; 31 | glm::vec2 position = glm::vec2(0.0f); 32 | glm::vec2 size = glm::vec2(BUTTON_SIZE); 33 | }; 34 | 35 | class Slider2D : public Panel2D { 36 | protected: 37 | 38 | Text2D* text_2d = nullptr; 39 | Text2D* text_2d_value = nullptr; 40 | 41 | int mode = SliderMode::VERTICAL; 42 | 43 | bool disabled = false; 44 | 45 | float hover_factor = 0.0f; 46 | float target_hover_factor = 0.0f; 47 | float timer = 0.0f; 48 | 49 | void* data = nullptr; 50 | 51 | virtual std::string value_to_string() = 0; 52 | 53 | public: 54 | 55 | Slider2D() {}; 56 | Slider2D(const std::string& name, const sSliderDescription& desc); 57 | 58 | void set_disabled(bool new_disabled); 59 | 60 | template 61 | void process_wheel_joystick(T wheel_multiplier, T joystick_multiplier); 62 | }; 63 | 64 | class FloatSlider2D : public Slider2D { 65 | 66 | float original_value = 0.0f; 67 | float current_value = 0.0f; 68 | float min_value = 0.0f; 69 | float max_value = 1.0f; 70 | 71 | int precision = 1; 72 | 73 | public: 74 | 75 | FloatSlider2D(const std::string& sg, const sSliderDescription& desc); 76 | 77 | void update(float delta_time) override; 78 | bool on_input(sInputData data) override; 79 | 80 | void set_value(float new_value); 81 | 82 | std::string value_to_string() override; 83 | }; 84 | 85 | class IntSlider2D : public Slider2D { 86 | 87 | int original_value = 0; 88 | int current_value = 0; 89 | int min_value = -10; 90 | int max_value = 10; 91 | 92 | public: 93 | 94 | IntSlider2D(const std::string& sg, const sSliderDescription& desc); 95 | 96 | void update(float delta_time) override; 97 | bool on_input(sInputData data) override; 98 | 99 | void set_value(int new_value); 100 | 101 | std::string value_to_string() override; 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /docs/reference/mesh-instance-3d.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/shaders/ui/ui_text_shadow.wgsl: -------------------------------------------------------------------------------- 1 | #include ui_includes.wgsl 2 | #include ../mesh_includes.wgsl 3 | 4 | #define GAMMA_CORRECTION 5 | 6 | @group(0) @binding(0) var mesh_data : InstanceData; 7 | 8 | #dynamic @group(1) @binding(0) var camera_data : CameraData; 9 | 10 | @group(2) @binding(1) var albedo: vec4f; 11 | 12 | @group(3) @binding(0) var ui_data : UIData; 13 | 14 | @vertex 15 | fn vs_main(in: VertexInput) -> VertexOutput { 16 | 17 | let instance_data : RenderMeshData = mesh_data.data[in.instance_id]; 18 | 19 | var out: VertexOutput; 20 | var world_position = instance_data.model * vec4f(in.position, 1.0); 21 | out.world_position = world_position.xyz; 22 | out.position = camera_data.view_projection * world_position; 23 | out.uv = in.uv; // forward to the fragment shader 24 | out.color = vec4(in.color, 1.0) * albedo; 25 | out.normal = in.normal; 26 | return out; 27 | } 28 | 29 | struct FragmentOutput { 30 | @location(0) color: vec4f 31 | } 32 | 33 | @fragment 34 | fn fs_main(in: VertexOutput) -> FragmentOutput { 35 | 36 | var dummy = camera_data.eye; 37 | 38 | var uvs : vec2f = vec2f(in.uv.x, in.uv.y); 39 | 40 | if(ui_data.clip_range < 0.0 && uvs.y < abs(ui_data.clip_range) ) { 41 | discard; 42 | } 43 | else if(ui_data.clip_range > 0.0 && uvs.y > ui_data.clip_range ) { 44 | discard; 45 | } 46 | 47 | var out: FragmentOutput; 48 | 49 | var ra : vec4f = vec4f(0.6); 50 | var si : vec2f = vec2f(0.98 * ui_data.aspect_ratio, 0.98); 51 | ra = min(ra, min(vec4f(si.x), vec4f(si.y))); 52 | 53 | uvs.y = 1.0 - in.uv.y; 54 | var pos : vec2f = vec2(uvs * 2.0 - 1.0); 55 | pos.x *= ui_data.aspect_ratio; 56 | 57 | let d : f32 = sdRoundedBox(pos, si * 0.975, ra * 0.975); 58 | let d_selected : f32 = sdRoundedBox(pos, si, ra); 59 | 60 | var alpha : f32 = 1.0 - smoothstep(0.0, 0.04, d); 61 | var alpha_selected : f32 = 1.0 - smoothstep(0.0, 0.08, d_selected); 62 | 63 | let is_selected : bool = (ui_data.flags & UI_DATA_SELECTED) == UI_DATA_SELECTED; 64 | let is_hovered : bool = (ui_data.flags & UI_DATA_HOVERED) == UI_DATA_HOVERED; 65 | var selected_mask : f32 = select(0.0, alpha_selected - alpha, is_selected); 66 | selected_mask = mix(0.0, selected_mask, in.uv.x); 67 | var color_factor : f32 = select(0.01, 0.05, is_hovered); 68 | color_factor = mix(color_factor, 0.75, selected_mask); 69 | 70 | var final_color = vec3f(color_factor); 71 | if(length(in.color.rgb) > 0) { 72 | final_color = mix(final_color, in.color.rgb, 1.0 - smoothstep(0.1, 0.4, (in.uv.x / (1.0 - in.uv.y)) * ui_data.aspect_ratio)); 73 | } 74 | 75 | let highlight_color : vec3f = mix(COLOR_HIGHLIGHT_LIGHT, COLOR_TERCIARY, uvs.y); 76 | final_color = mix(final_color, highlight_color, selected_mask); 77 | 78 | if (GAMMA_CORRECTION == 1) { 79 | final_color = pow(final_color, vec3f(1.0 / 2.2)); 80 | } 81 | 82 | 83 | out.color = vec4f(final_color, alpha_selected); 84 | 85 | return out; 86 | } -------------------------------------------------------------------------------- /src/framework/nodes/container_2d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/ui/ui_utils.h" 4 | #include "framework/colors.h" 5 | #include "graphics/mesh.h" 6 | #include "framework/nodes/panel_2d.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace ui { 14 | 15 | class Container2D : public Panel2D { 16 | public: 17 | 18 | glm::vec2 fixed_size = {}; 19 | bool use_fixed_size = false; 20 | 21 | bool centered = false; 22 | bool can_hover = false; 23 | 24 | glm::vec2 padding = { 0.0f, 0.0f }; 25 | glm::vec2 item_margin = { 0.0f, 0.0f }; 26 | 27 | Container2D() {}; 28 | Container2D(const std::string& name, const glm::vec2& p, const glm::vec2& s = { 0.0f, 0.0f }, uint32_t flags = 0u, const Color& c = colors::WHITE); 29 | 30 | void update(float delta_time) override; 31 | bool on_input(sInputData data) override; 32 | void on_children_changed() override; 33 | 34 | void set_hoverable(bool value); 35 | void set_centered(bool value); 36 | void set_fixed_size(const glm::vec2& new_size); 37 | }; 38 | 39 | class HContainer2D : public Container2D { 40 | public: 41 | HContainer2D() {}; 42 | HContainer2D(const std::string& name, const glm::vec2& p, uint32_t flags = 0u, const Color& c = colors::WHITE); 43 | 44 | void on_children_changed() override; 45 | }; 46 | 47 | class VContainer2D : public Container2D { 48 | 49 | public: 50 | VContainer2D() {}; 51 | VContainer2D(const std::string& name, const glm::vec2& p, uint32_t flags = 0u, const Color& c = colors::WHITE); 52 | 53 | void on_children_changed() override; 54 | }; 55 | 56 | class CircleContainer2D : public Container2D { 57 | public: 58 | CircleContainer2D() {}; 59 | CircleContainer2D(const std::string& name, const glm::vec2& p, uint32_t flags = 0u, const Color& c = colors::GRAY); 60 | 61 | void update(float delta_time) override; 62 | bool on_input(sInputData data) override; 63 | void on_children_changed() override; 64 | }; 65 | 66 | class ItemGroup2D : public HContainer2D { 67 | public: 68 | 69 | ItemGroup2D(const std::string& name, uint32_t flags = 0u, const glm::vec2& pos = { 0.0f, 0.0f }, const Color& color = colors::GRAY); 70 | 71 | float get_number_of_items(); 72 | void set_number_of_items(float number); 73 | 74 | void on_children_changed() override; 75 | }; 76 | 77 | class ImageLabel2D : public HContainer2D { 78 | 79 | uint8_t mask = 0; 80 | 81 | public: 82 | 83 | Text2D* text = nullptr; 84 | 85 | ImageLabel2D(const std::string& p_text, const std::string& image_path, uint8_t mask = 0, const glm::vec2& scale = { 1.f, 1.f }, float text_scale = 16.0f, const glm::vec2& p = { 0.f, 0.f }); 86 | 87 | void set_text(const std::string& p_text) { text->set_text(p_text); } 88 | 89 | uint8_t get_mask() { return mask; } 90 | }; 91 | } 92 | -------------------------------------------------------------------------------- /src/framework/camera/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/math/math_utils.h" 4 | 5 | enum eCameraType { 6 | CAMERA_ORBIT, 7 | CAMERA_FLYOVER 8 | }; 9 | 10 | class Camera { 11 | public: 12 | 13 | enum eCameraProjectionType { 14 | PERSPECTIVE, 15 | ORTHOGRAPHIC 16 | }; 17 | 18 | Camera() = default; 19 | 20 | virtual void update(float delta_time) {}; 21 | 22 | void update_view_matrix(); 23 | void update_projection_matrix(); 24 | void update_view_projection_matrix(); 25 | 26 | virtual void look_at(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up, bool reset_internals = true); 27 | 28 | glm::vec3 screen_to_ray(const glm::vec2& mouse_position); 29 | 30 | void set_perspective(float fov, float aspect, float z_near, float z_far); 31 | void set_orthographic(float left, float right, float bottom, float top, float z_near, float z_far); 32 | 33 | void set_view(const glm::mat4x4& view, bool update_view_projection = true); 34 | void set_projection(const glm::mat4x4& projection, bool update_view_projection = true); 35 | void set_view_projection(const glm::mat4x4& view_projection); 36 | 37 | void set_eye(const glm::vec3& new_eye); 38 | void set_center(const glm::vec3& new_center); 39 | void set_up(const glm::vec3& new_up); 40 | 41 | void set_speed(float speed) { this->speed = speed; } 42 | void set_mouse_sensitivity(float mouse_sensitivity) { this->mouse_sensitivity = mouse_sensitivity; } 43 | 44 | glm::vec3 get_local_vector(const glm::vec3& vector); 45 | 46 | const glm::vec3& get_eye() const { return eye; } 47 | const glm::vec3& get_center() const { return center; } 48 | const glm::vec3& get_up() const { return up; } 49 | 50 | float get_fov() const { return fov; } 51 | float get_aspect() const { return aspect; } 52 | float get_near() const { return z_near; } 53 | float get_far() const { return z_far; } 54 | float get_left() const { return left; } 55 | float get_right() const { return right; } 56 | float get_top() const { return top; } 57 | float get_bottom() const { return bottom; } 58 | float get_speed() const { return speed; } 59 | float get_mouse_sensitivity() const { return mouse_sensitivity; } 60 | 61 | const glm::mat4x4& get_view() const { return view; } 62 | const glm::mat4x4& get_projection() const { return projection; } 63 | const glm::mat4x4& get_view_projection() const { return view_projection; } 64 | 65 | protected: 66 | 67 | glm::mat4x4 view; 68 | glm::mat4x4 projection; 69 | glm::mat4x4 view_projection; 70 | 71 | float left; 72 | float right; 73 | float top; 74 | float bottom; 75 | 76 | eCameraProjectionType type; 77 | 78 | float fov; 79 | float aspect; 80 | float z_near; 81 | float z_far; 82 | 83 | float speed = 1.0f; 84 | float mouse_sensitivity = 0.01f; 85 | 86 | #ifdef __EMSCRIPTEN__ 87 | public: 88 | #endif 89 | 90 | glm::vec3 eye = {}; 91 | glm::vec3 center = {}; 92 | glm::vec3 up = { 0.0f, 1.0f, 0.0f }; 93 | }; 94 | -------------------------------------------------------------------------------- /data/shaders/ui/ui_includes.wgsl: -------------------------------------------------------------------------------- 1 | struct UIData { 2 | flags: u32, 3 | hover_time : f32, 4 | aspect_ratio : f32, 5 | data_value : f32, 6 | 7 | data_vec : vec3f, 8 | clip_range : f32, 9 | 10 | xr_size : vec2f, 11 | xr_position : vec2f 12 | }; 13 | 14 | const UI_DATA_HOVERED: u32 = 1u << 0u; 15 | const UI_DATA_PRESSED: u32 = 1u << 1u; 16 | const UI_DATA_SELECTED: u32 = 1u << 2u; 17 | const UI_DATA_COLOR_BUTTON: u32 = 1u << 3u; 18 | const UI_DATA_DISABLED: u32 = 1u << 4u; 19 | 20 | const COLOR_PRIMARY = pow(vec3f(0.976, 0.976, 0.976), vec3f(2.2)); 21 | const COLOR_SECONDARY = pow(vec3f(0.967, 0.882, 0.863), vec3f(2.2)); 22 | const COLOR_TERCIARY = pow(vec3f(1.0, 0.404, 0.0), vec3f(2.2)); 23 | const COLOR_HIGHLIGHT_LIGHT = pow(vec3f(0.467, 0.333, 0.933), vec3f(2.2)); 24 | const COLOR_HIGHLIGHT = pow(vec3f(0.26, 0.2, 0.533), vec3f(2.2)); 25 | const COLOR_HIGHLIGHT_DARK = pow(vec3f(0.082, 0.086, 0.196), vec3f(2.2)); 26 | const COLOR_DARK = pow(vec3f(0.172, 0.172, 0.172), vec3f(2.2)); 27 | 28 | const EPSILON : f32 = 0.02; 29 | const PI : f32 = 3.14159265359; 30 | const OUTLINECOLOR : vec4f = vec4f(0.3, 0.3, 0.3, 0.9); 31 | 32 | fn remap_range(oldValue : f32, oldMin: f32, oldMax : f32, newMin : f32, newMax : f32) -> f32 33 | { 34 | return (((oldValue - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin; 35 | } 36 | 37 | fn sdRoundedBox( p : vec2f, b : vec2f, cr : vec4f ) -> f32 38 | { 39 | var r : vec2f = select(cr.zw, cr.xy, p.x > 0.0); 40 | r.x = select(r.y, r.x, p.y > 0.0); 41 | var q : vec2f = abs(p) - b + r.x; 42 | return min(max(q.x,q.y),0.0) + length(max(q,vec2f(0.0))) - r.x; 43 | } 44 | 45 | fn calculate_triangle_weight( p : vec2f, v1 : vec2f, v2 : vec2f, v3 : vec2f ) -> vec3f 46 | { 47 | var weight : vec3f; 48 | weight.x = ((v2.y-v3.y)*(p.x-v3.x)+(v3.x-v2.x)*(p.y-v3.y)) / ((v2.y-v3.y)*(v1.x-v3.x)+(v3.x-v2.x)*(v1.y-v3.y)); 49 | weight.y = ((v3.y-v1.y)*(p.x-v3.x)+(v1.x-v3.x)*(p.y-v3.y)) / ((v2.y-v3.y)*(v1.x-v3.x)+(v3.x-v2.x)*(v1.y-v3.y)); 50 | weight.z = 1.0 - weight.x - weight.y; 51 | return weight; 52 | } 53 | 54 | fn draw_line( uv : vec2f, p1 : vec2f, p2 : vec2f, color : vec4f, thickness : f32 ) -> vec4f 55 | { 56 | var final_color : vec4f = color; 57 | let t = thickness * 0.5; 58 | var dir = p2 - p1; 59 | let line_length : f32 = length(dir); 60 | dir = normalize(dir); 61 | let to_uv : vec2f = uv - p1; 62 | let project_length : f32 = dot(dir, to_uv); 63 | var p2_line_distance : f32 = length(to_uv-dir*project_length); 64 | p2_line_distance = smoothstep(t + EPSILON, t, p2_line_distance); 65 | var p2_end_distance : f32 = select(project_length-line_length, abs(project_length), project_length <= 0.0); 66 | p2_end_distance = smoothstep(t, t - EPSILON * 0.5, p2_end_distance); 67 | final_color = vec4f(final_color.xyz * mix(p2_line_distance, 0.0, 0.01), final_color.a); 68 | final_color.a *= min(p2_line_distance, p2_end_distance); 69 | return final_color; 70 | } 71 | 72 | fn draw_point( uv : vec2f, p : vec2f, s : f32) -> vec4f 73 | { 74 | let alpha : f32 = smoothstep(0.002,0.015, abs(length(uv - p) - s)); 75 | return vec4(vec3(1.0), 1.0 - alpha); 76 | } --------------------------------------------------------------------------------