ADD: Floating origin system

This commit is contained in:
2025-12-14 21:37:49 +09:00
parent eb5a04e95a
commit d6ad2bf252
18 changed files with 728 additions and 69 deletions

View File

@@ -16,9 +16,9 @@ Current structure:
- SSR - SSR
- FXAA - FXAA
- Bloom - Bloom
- Floating origin with double precision coordinate system
Work-In-Progress Work-In-Progress
- [ ] Floating origin with double precision coordinate system
- [ ] Planet Rendering - [ ] Planet Rendering
## Build prequsites ## Build prequsites

View File

@@ -4,6 +4,7 @@ add_executable (vulkan_engine
main.cpp main.cpp
# core root # core root
core/types.h core/types.h
core/world.h
core/config.h core/config.h
core/context.h core/context.h
core/context.cpp core/context.cpp

View File

@@ -104,6 +104,32 @@ AsyncAssetLoader::JobID AsyncAssetLoader::load_gltf_async(const std::string &sce
return id; return id;
} }
AsyncAssetLoader::JobID AsyncAssetLoader::load_gltf_async(const std::string &scene_name,
const std::string &model_relative_path,
const WorldVec3 &translation_world,
const glm::quat &rotation,
const glm::vec3 &scale,
bool preload_textures)
{
JobID id = load_gltf_async(scene_name, model_relative_path, glm::mat4(1.0f), preload_textures);
if (id == 0)
{
return 0;
}
std::lock_guard<std::mutex> lock(_jobs_mutex);
auto it = _jobs.find(id);
if (it != _jobs.end() && it->second)
{
Job &job = *it->second;
job.has_world_trs = true;
job.translation_world = translation_world;
job.rotation = rotation;
job.scale = scale;
}
return id;
}
bool AsyncAssetLoader::get_job_status(JobID id, JobState &out_state, float &out_progress, std::string *out_error) bool AsyncAssetLoader::get_job_status(JobID id, JobState &out_state, float &out_progress, std::string *out_error)
{ {
std::lock_guard<std::mutex> lock(_jobs_mutex); std::lock_guard<std::mutex> lock(_jobs_mutex);
@@ -234,6 +260,13 @@ void AsyncAssetLoader::pump_main_thread(SceneManager &scene)
job->scene->debugName = job->model_relative_path; job->scene->debugName = job->model_relative_path;
} }
scene.addGLTFInstance(job->scene_name, job->scene, job->transform); scene.addGLTFInstance(job->scene_name, job->scene, job->transform);
if (job->has_world_trs)
{
scene.setGLTFInstanceTRSWorld(job->scene_name,
job->translation_world,
job->rotation,
job->scale);
}
// Optionally preload textures (same logic as addGLTFInstance) // Optionally preload textures (same logic as addGLTFInstance)
if (job->preload_textures && _textures && _engine && _engine->_resourceManager) if (job->preload_textures && _textures && _engine && _engine->_resourceManager)

View File

@@ -11,8 +11,11 @@
#include <thread> #include <thread>
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/vec3.hpp>
#include "scene/vk_loader.h" #include "scene/vk_loader.h"
#include "core/world.h"
#include "core/assets/texture_cache.h" #include "core/assets/texture_cache.h"
class VulkanEngine; class VulkanEngine;
@@ -41,6 +44,13 @@ public:
const glm::mat4 &transform, const glm::mat4 &transform,
bool preload_textures = false); bool preload_textures = false);
JobID load_gltf_async(const std::string &scene_name,
const std::string &model_relative_path,
const WorldVec3 &translation_world,
const glm::quat &rotation,
const glm::vec3 &scale,
bool preload_textures = false);
bool get_job_status(JobID id, JobState &out_state, float &out_progress, std::string *out_error = nullptr); bool get_job_status(JobID id, JobState &out_state, float &out_progress, std::string *out_error = nullptr);
// Main-thread integration: commit completed jobs into the SceneManager. // Main-thread integration: commit completed jobs into the SceneManager.
@@ -66,6 +76,10 @@ private:
std::string scene_name; std::string scene_name;
std::string model_relative_path; std::string model_relative_path;
glm::mat4 transform{1.0f}; glm::mat4 transform{1.0f};
bool has_world_trs{false};
WorldVec3 translation_world{0.0, 0.0, 0.0};
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f};
bool preload_textures{false}; bool preload_textures{false};
std::shared_ptr<LoadedGLTF> scene; std::shared_ptr<LoadedGLTF> scene;

View File

@@ -533,7 +533,7 @@ void TextureCache::worker_loop()
{ {
ktx_size_t off = 0, len = 0; ktx_size_t off = 0, len = 0;
ktxTexture_GetImageOffset(ktxTexture(ktex), mip, 0, 0, &off); ktxTexture_GetImageOffset(ktxTexture(ktex), mip, 0, 0, &off);
ktxTexture_GetImageSize(ktxTexture(ktex), mip, &len); len = ktxTexture_GetImageSize(ktxTexture(ktex), mip);
uint32_t w = std::max(1u, baseW >> mip); uint32_t w = std::max(1u, baseW >> mip);
uint32_t h = std::max(1u, baseH >> mip); uint32_t h = std::max(1u, baseH >> mip);
out.ktx.levels.push_back({ static_cast<uint64_t>(off), static_cast<uint64_t>(len), w, h }); out.ktx.levels.push_back({ static_cast<uint64_t>(off), static_cast<uint64_t>(len), w, h });

View File

@@ -587,6 +587,36 @@ uint32_t VulkanEngine::loadGLTFAsync(const std::string &sceneName,
return _asyncLoader->load_gltf_async(sceneName, resolvedPath, transform, preloadTextures); return _asyncLoader->load_gltf_async(sceneName, resolvedPath, transform, preloadTextures);
} }
uint32_t VulkanEngine::loadGLTFAsync(const std::string &sceneName,
const std::string &modelRelativePath,
const WorldVec3 &translationWorld,
const glm::quat &rotation,
const glm::vec3 &scale,
bool preloadTextures)
{
if (!_asyncLoader || !_assetManager || !_sceneManager)
{
return 0;
}
const std::string resolvedPath = _assetManager->modelPath(modelRelativePath);
if (!file_exists_nothrow(resolvedPath))
{
fmt::println("[Engine] Failed to enqueue async glTF load for scene '{}' model file not found (requested='{}', resolved='{}')",
sceneName,
modelRelativePath,
resolvedPath);
return 0;
}
return _asyncLoader->load_gltf_async(sceneName,
resolvedPath,
translationWorld,
rotation,
scale,
preloadTextures);
}
void VulkanEngine::preloadInstanceTextures(const std::string &instanceName) void VulkanEngine::preloadInstanceTextures(const std::string &instanceName)
{ {
if (!_textureCache || !_sceneManager) if (!_textureCache || !_sceneManager)
@@ -746,16 +776,16 @@ void VulkanEngine::draw()
// Update IBL based on camera position and user-defined reflection volumes. // Update IBL based on camera position and user-defined reflection volumes.
if (_iblManager && _sceneManager) if (_iblManager && _sceneManager)
{ {
glm::vec3 camPos = _sceneManager->getMainCamera().position; WorldVec3 camPosWorld = _sceneManager->getMainCamera().position_world;
int newVolume = -1; int newVolume = -1;
for (size_t i = 0; i < _iblVolumes.size(); ++i) for (size_t i = 0; i < _iblVolumes.size(); ++i)
{ {
const IBLVolume &v = _iblVolumes[i]; const IBLVolume &v = _iblVolumes[i];
if (!v.enabled) continue; if (!v.enabled) continue;
glm::vec3 local = camPos - v.center; WorldVec3 local = camPosWorld - v.center_world;
if (std::abs(local.x) <= v.halfExtents.x && if (std::abs(local.x) <= static_cast<double>(v.halfExtents.x) &&
std::abs(local.y) <= v.halfExtents.y && std::abs(local.y) <= static_cast<double>(v.halfExtents.y) &&
std::abs(local.z) <= v.halfExtents.z) std::abs(local.z) <= static_cast<double>(v.halfExtents.z))
{ {
newVolume = static_cast<int>(i); newVolume = static_cast<int>(i);
break; break;
@@ -800,7 +830,7 @@ void VulkanEngine::draw()
if (_sceneManager && _mousePosPixels.x >= 0.0f && _mousePosPixels.y >= 0.0f) if (_sceneManager && _mousePosPixels.x >= 0.0f && _mousePosPixels.y >= 0.0f)
{ {
RenderObject hoverObj{}; RenderObject hoverObj{};
glm::vec3 hoverPos{}; WorldVec3 hoverPos{};
if (_sceneManager->pick(_mousePosPixels, hoverObj, hoverPos)) if (_sceneManager->pick(_mousePosPixels, hoverObj, hoverPos))
{ {
_hoverPick.mesh = hoverObj.sourceMesh; _hoverPick.mesh = hoverObj.sourceMesh;
@@ -1225,7 +1255,7 @@ void VulkanEngine::run()
if (_sceneManager) if (_sceneManager)
{ {
RenderObject hitObject{}; RenderObject hitObject{};
glm::vec3 hitPos{}; WorldVec3 hitPos{};
if (_sceneManager->pick(releasePos, hitObject, hitPos)) if (_sceneManager->pick(releasePos, hitObject, hitPos))
{ {
_lastPick.mesh = hitObject.sourceMesh; _lastPick.mesh = hitObject.sourceMesh;
@@ -1269,8 +1299,8 @@ void VulkanEngine::run()
info.ownerType = obj.ownerType; info.ownerType = obj.ownerType;
info.ownerName = obj.ownerName; info.ownerName = obj.ownerName;
// Use bounds origin transformed to world as a representative point. // Use bounds origin transformed to world as a representative point.
glm::vec3 centerWorld = glm::vec3(obj.transform * glm::vec4(obj.bounds.origin, 1.0f)); glm::vec3 centerLocal = glm::vec3(obj.transform * glm::vec4(obj.bounds.origin, 1.0f));
info.worldPos = centerWorld; info.worldPos = local_to_world(centerLocal, _sceneManager->get_world_origin());
info.worldTransform = obj.transform; info.worldTransform = obj.transform;
info.firstIndex = obj.firstIndex; info.firstIndex = obj.firstIndex;
info.indexCount = obj.indexCount; info.indexCount = obj.indexCount;
@@ -1365,7 +1395,8 @@ void VulkanEngine::run()
if (_sceneManager->resolveObjectID(pickedID, picked)) if (_sceneManager->resolveObjectID(pickedID, picked))
{ {
// Fallback hit position: object origin in world space (can refine later) // Fallback hit position: object origin in world space (can refine later)
glm::vec3 fallbackPos = glm::vec3(picked.transform[3]); glm::vec3 fallbackLocal = glm::vec3(picked.transform[3]);
WorldVec3 fallbackPos = local_to_world(fallbackLocal, _sceneManager->get_world_origin());
_lastPick.mesh = picked.sourceMesh; _lastPick.mesh = picked.sourceMesh;
_lastPick.scene = picked.sourceScene; _lastPick.scene = picked.sourceScene;
_lastPick.node = picked.sourceNode; _lastPick.node = picked.sourceNode;

View File

@@ -4,6 +4,7 @@
#pragma once #pragma once
#include <core/types.h> #include <core/types.h>
#include <core/world.h>
#include <vector> #include <vector>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@@ -123,7 +124,7 @@ public:
// Simple world-space IBL reflection volumes (axis-aligned boxes). // Simple world-space IBL reflection volumes (axis-aligned boxes).
struct IBLVolume struct IBLVolume
{ {
glm::vec3 center{0.0f, 0.0f, 0.0f}; WorldVec3 center_world{0.0, 0.0, 0.0};
glm::vec3 halfExtents{10.0f, 10.0f, 10.0f}; glm::vec3 halfExtents{10.0f, 10.0f, 10.0f};
IBLPaths paths{}; // HDRI paths for this volume IBLPaths paths{}; // HDRI paths for this volume
bool enabled{true}; bool enabled{true};
@@ -149,7 +150,7 @@ public:
Node *node = nullptr; Node *node = nullptr;
RenderObject::OwnerType ownerType = RenderObject::OwnerType::None; RenderObject::OwnerType ownerType = RenderObject::OwnerType::None;
std::string ownerName; std::string ownerName;
glm::vec3 worldPos{0.0f}; WorldVec3 worldPos{0.0, 0.0, 0.0};
glm::mat4 worldTransform{1.0f}; glm::mat4 worldTransform{1.0f};
uint32_t indexCount = 0; uint32_t indexCount = 0;
uint32_t firstIndex = 0; uint32_t firstIndex = 0;
@@ -242,6 +243,13 @@ public:
const glm::mat4 &transform = glm::mat4(1.f), const glm::mat4 &transform = glm::mat4(1.f),
bool preloadTextures = false); bool preloadTextures = false);
uint32_t loadGLTFAsync(const std::string &sceneName,
const std::string &modelRelativePath,
const WorldVec3 &translationWorld,
const glm::quat &rotation,
const glm::vec3 &scale,
bool preloadTextures = false);
// Preload textures for an already-loaded scene instance so they are // Preload textures for an already-loaded scene instance so they are
// available before the object becomes visible (visibility-driven loading). // available before the object becomes visible (visibility-driven loading).
void preloadInstanceTextures(const std::string &instanceName); void preloadInstanceTextures(const std::string &instanceName);

View File

@@ -245,7 +245,7 @@ namespace
VulkanEngine::IBLVolume vol{}; VulkanEngine::IBLVolume vol{};
if (eng->_sceneManager) if (eng->_sceneManager)
{ {
vol.center = eng->_sceneManager->getMainCamera().position; vol.center_world = eng->_sceneManager->getMainCamera().position_world;
} }
vol.halfExtents = glm::vec3(10.0f, 10.0f, 10.0f); vol.halfExtents = glm::vec3(10.0f, 10.0f, 10.0f);
vol.paths = eng->_globalIBLPaths; vol.paths = eng->_globalIBLPaths;
@@ -259,7 +259,13 @@ namespace
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Volume %zu", i); ImGui::Text("Volume %zu", i);
ImGui::Checkbox("Enabled", &vol.enabled); ImGui::Checkbox("Enabled", &vol.enabled);
ImGui::InputFloat3("Center", &vol.center.x); {
double c[3] = {vol.center_world.x, vol.center_world.y, vol.center_world.z};
if (ImGui::InputScalarN("Center (world)", ImGuiDataType_Double, c, 3, nullptr, nullptr, "%.3f"))
{
vol.center_world = WorldVec3(c[0], c[1], c[2]);
}
}
ImGui::InputFloat3("Half Extents", &vol.halfExtents.x); ImGui::InputFloat3("Half Extents", &vol.halfExtents.x);
// Simple path editors; store absolute or engine-local paths. // Simple path editors; store absolute or engine-local paths.
@@ -337,6 +343,17 @@ namespace
ImGui::Text("Swapchain: %ux%u", scExt.width, scExt.height); ImGui::Text("Swapchain: %ux%u", scExt.width, scExt.height);
ImGui::Text("Draw fmt: %s", string_VkFormat(eng->_swapchainManager->drawImage().imageFormat)); ImGui::Text("Draw fmt: %s", string_VkFormat(eng->_swapchainManager->drawImage().imageFormat));
ImGui::Text("Swap fmt: %s", string_VkFormat(eng->_swapchainManager->swapchainImageFormat())); ImGui::Text("Swap fmt: %s", string_VkFormat(eng->_swapchainManager->swapchainImageFormat()));
if (eng->_sceneManager)
{
ImGui::Separator();
WorldVec3 origin = eng->_sceneManager->get_world_origin();
WorldVec3 camWorld = eng->_sceneManager->getMainCamera().position_world;
glm::vec3 camLocal = eng->_sceneManager->get_camera_local_position();
ImGui::Text("Origin (world): (%.3f, %.3f, %.3f)", origin.x, origin.y, origin.z);
ImGui::Text("Camera (world): (%.3f, %.3f, %.3f)", camWorld.x, camWorld.y, camWorld.z);
ImGui::Text("Camera (local): (%.3f, %.3f, %.3f)", camLocal.x, camLocal.y, camLocal.z);
}
} }
// Texture streaming + budget UI // Texture streaming + budget UI
@@ -956,18 +973,18 @@ namespace
SceneManager::PointLight pl{}; SceneManager::PointLight pl{};
if (sceneMgr->getPointLight(static_cast<size_t>(selectedLight), pl)) if (sceneMgr->getPointLight(static_cast<size_t>(selectedLight), pl))
{ {
float pos[3] = {pl.position.x, pl.position.y, pl.position.z}; double pos[3] = {pl.position_world.x, pl.position_world.y, pl.position_world.z};
float col[3] = {pl.color.r, pl.color.g, pl.color.b}; float col[3] = {pl.color.r, pl.color.g, pl.color.b};
bool changed = false; bool changed = false;
changed |= ImGui::InputFloat3("Position", pos); changed |= ImGui::InputScalarN("Position (world)", ImGuiDataType_Double, pos, 3, nullptr, nullptr, "%.3f");
changed |= ImGui::SliderFloat("Radius", &pl.radius, 0.1f, 1000.0f); changed |= ImGui::SliderFloat("Radius", &pl.radius, 0.1f, 1000.0f);
changed |= ImGui::ColorEdit3("Color", col); changed |= ImGui::ColorEdit3("Color", col);
changed |= ImGui::SliderFloat("Intensity", &pl.intensity, 0.0f, 100.0f); changed |= ImGui::SliderFloat("Intensity", &pl.intensity, 0.0f, 100.0f);
if (changed) if (changed)
{ {
pl.position = glm::vec3(pos[0], pos[1], pos[2]); pl.position_world = WorldVec3(pos[0], pos[1], pos[2]);
pl.color = glm::vec3(col[0], col[1], col[2]); pl.color = glm::vec3(col[0], col[1], col[2]);
sceneMgr->setPointLight(static_cast<size_t>(selectedLight), pl); sceneMgr->setPointLight(static_cast<size_t>(selectedLight), pl);
} }
@@ -983,12 +1000,12 @@ namespace
// Controls for adding a new light // Controls for adding a new light
ImGui::Separator(); ImGui::Separator();
ImGui::TextUnformatted("Add point light"); ImGui::TextUnformatted("Add point light");
static float newPos[3] = {0.0f, 1.0f, 0.0f}; static double newPos[3] = {0.0, 1.0, 0.0};
static float newRadius = 10.0f; static float newRadius = 10.0f;
static float newColor[3] = {1.0f, 1.0f, 1.0f}; static float newColor[3] = {1.0f, 1.0f, 1.0f};
static float newIntensity = 5.0f; static float newIntensity = 5.0f;
ImGui::InputFloat3("New position", newPos); ImGui::InputScalarN("New position (world)", ImGuiDataType_Double, newPos, 3, nullptr, nullptr, "%.3f");
ImGui::SliderFloat("New radius", &newRadius, 0.1f, 1000.0f); ImGui::SliderFloat("New radius", &newRadius, 0.1f, 1000.0f);
ImGui::ColorEdit3("New color", newColor); ImGui::ColorEdit3("New color", newColor);
ImGui::SliderFloat("New intensity", &newIntensity, 0.0f, 100.0f); ImGui::SliderFloat("New intensity", &newIntensity, 0.0f, 100.0f);
@@ -996,7 +1013,7 @@ namespace
if (ImGui::Button("Add point light")) if (ImGui::Button("Add point light"))
{ {
SceneManager::PointLight pl{}; SceneManager::PointLight pl{};
pl.position = glm::vec3(newPos[0], newPos[1], newPos[2]); pl.position_world = WorldVec3(newPos[0], newPos[1], newPos[2]);
pl.radius = newRadius; pl.radius = newRadius;
pl.color = glm::vec3(newColor[0], newColor[1], newColor[2]); pl.color = glm::vec3(newColor[0], newColor[1], newColor[2]);
pl.intensity = newIntensity; pl.intensity = newIntensity;
@@ -1214,7 +1231,7 @@ namespace
if (pick->ownerType == RenderObject::OwnerType::MeshInstance) if (pick->ownerType == RenderObject::OwnerType::MeshInstance)
{ {
if (sceneMgr->getMeshInstanceTransform(pick->ownerName, targetTransform)) if (sceneMgr->getMeshInstanceTransformLocal(pick->ownerName, targetTransform))
{ {
target = GizmoTarget::MeshInstance; target = GizmoTarget::MeshInstance;
ImGui::Text("Editing mesh instance: %s", pick->ownerName.c_str()); ImGui::Text("Editing mesh instance: %s", pick->ownerName.c_str());
@@ -1222,7 +1239,7 @@ namespace
} }
else if (pick->ownerType == RenderObject::OwnerType::GLTFInstance) else if (pick->ownerType == RenderObject::OwnerType::GLTFInstance)
{ {
if (sceneMgr->getGLTFInstanceTransform(pick->ownerName, targetTransform)) if (sceneMgr->getGLTFInstanceTransformLocal(pick->ownerName, targetTransform))
{ {
target = GizmoTarget::GLTFInstance; target = GizmoTarget::GLTFInstance;
ImGui::Text("Editing glTF instance: %s", pick->ownerName.c_str()); ImGui::Text("Editing glTF instance: %s", pick->ownerName.c_str());
@@ -1270,8 +1287,8 @@ namespace
: 1.0f; : 1.0f;
// Distance from camera to object; clamp to avoid degenerate planes. // Distance from camera to object; clamp to avoid degenerate planes.
glm::vec3 camPos = cam.position; glm::vec3 camPos = sceneMgr->get_camera_local_position();
glm::vec3 objPos = pick->worldPos; glm::vec3 objPos = glm::vec3(targetTransform[3]);
float dist = glm::length(objPos - camPos); float dist = glm::length(objPos - camPos);
if (!std::isfinite(dist) || dist <= 0.0f) if (!std::isfinite(dist) || dist <= 0.0f)
{ {
@@ -1282,7 +1299,7 @@ namespace
float nearPlane = glm::max(0.05f, dist * 0.05f); float nearPlane = glm::max(0.05f, dist * 0.05f);
float farPlane = glm::max(nearPlane * 50.0f, dist * 2.0f); float farPlane = glm::max(nearPlane * 50.0f, dist * 2.0f);
glm::mat4 view = cam.getViewMatrix(); glm::mat4 view = cam.getViewMatrix(sceneMgr->get_camera_local_position());
glm::mat4 proj = glm::perspective(fovRad, aspect, nearPlane, farPlane); glm::mat4 proj = glm::perspective(fovRad, aspect, nearPlane, farPlane);
glm::mat4 before = targetTransform; glm::mat4 before = targetTransform;
@@ -1313,10 +1330,10 @@ namespace
switch (target) switch (target)
{ {
case GizmoTarget::MeshInstance: case GizmoTarget::MeshInstance:
sceneMgr->setMeshInstanceTransform(pick->ownerName, targetTransform); sceneMgr->setMeshInstanceTransformLocal(pick->ownerName, targetTransform);
break; break;
case GizmoTarget::GLTFInstance: case GizmoTarget::GLTFInstance:
sceneMgr->setGLTFInstanceTransform(pick->ownerName, targetTransform); sceneMgr->setGLTFInstanceTransformLocal(pick->ownerName, targetTransform);
break; break;
default: default:
break; break;
@@ -1324,7 +1341,7 @@ namespace
// Keep pick debug info roughly in sync. // Keep pick debug info roughly in sync.
pick->worldTransform = targetTransform; pick->worldTransform = targetTransform;
pick->worldPos = glm::vec3(targetTransform[3]); pick->worldPos = local_to_world(glm::vec3(targetTransform[3]), sceneMgr->get_world_origin());
} }
} }
} // namespace } // namespace

View File

@@ -37,6 +37,25 @@ Transform Transform::from_matrix(const glm::mat4& m)
return t; return t;
} }
glm::mat4 TransformD::to_matrix() const
{
glm::mat4 T = glm::translate(glm::mat4(1.0f), glm::vec3(position));
glm::mat4 R = glm::mat4_cast(rotation);
glm::mat4 S = glm::scale(glm::mat4(1.0f), scale);
return T * R * S;
}
TransformD TransformD::from_matrix(const glm::mat4& m)
{
TransformD t;
glm::vec3 skew;
glm::vec4 perspective;
glm::vec3 pos{};
glm::decompose(m, t.scale, t.rotation, pos, skew, perspective);
t.position = glm::dvec3(pos);
return t;
}
// ============================================================================ // ============================================================================
// Engine Implementation // Engine Implementation
// ============================================================================ // ============================================================================
@@ -254,7 +273,19 @@ void Engine::set_global_ibl_paths(const IBLPaths& paths)
size_t Engine::add_ibl_volume(const IBLVolume& volume) size_t Engine::add_ibl_volume(const IBLVolume& volume)
{ {
VulkanEngine::IBLVolume v; VulkanEngine::IBLVolume v;
v.center = volume.center; v.center_world = WorldVec3(volume.center);
v.halfExtents = volume.halfExtents;
v.paths = to_internal_ibl_paths(volume.paths);
v.enabled = volume.enabled;
_engine->_iblVolumes.push_back(v);
return _engine->_iblVolumes.size() - 1;
}
size_t Engine::add_ibl_volume(const IBLVolumeD& volume)
{
VulkanEngine::IBLVolume v;
v.center_world = WorldVec3(volume.center);
v.halfExtents = volume.halfExtents; v.halfExtents = volume.halfExtents;
v.paths = to_internal_ibl_paths(volume.paths); v.paths = to_internal_ibl_paths(volume.paths);
v.enabled = volume.enabled; v.enabled = volume.enabled;
@@ -285,7 +316,19 @@ bool Engine::get_ibl_volume(size_t index, IBLVolume& out) const
if (index >= _engine->_iblVolumes.size()) return false; if (index >= _engine->_iblVolumes.size()) return false;
const auto& v = _engine->_iblVolumes[index]; const auto& v = _engine->_iblVolumes[index];
out.center = v.center; out.center = glm::vec3(v.center_world);
out.halfExtents = v.halfExtents;
out.paths = from_internal_ibl_paths(v.paths);
out.enabled = v.enabled;
return true;
}
bool Engine::get_ibl_volume(size_t index, IBLVolumeD& out) const
{
if (index >= _engine->_iblVolumes.size()) return false;
const auto& v = _engine->_iblVolumes[index];
out.center = v.center_world;
out.halfExtents = v.halfExtents; out.halfExtents = v.halfExtents;
out.paths = from_internal_ibl_paths(v.paths); out.paths = from_internal_ibl_paths(v.paths);
out.enabled = v.enabled; out.enabled = v.enabled;
@@ -297,7 +340,19 @@ bool Engine::set_ibl_volume(size_t index, const IBLVolume& volume)
if (index >= _engine->_iblVolumes.size()) return false; if (index >= _engine->_iblVolumes.size()) return false;
auto& v = _engine->_iblVolumes[index]; auto& v = _engine->_iblVolumes[index];
v.center = volume.center; v.center_world = WorldVec3(volume.center);
v.halfExtents = volume.halfExtents;
v.paths = to_internal_ibl_paths(volume.paths);
v.enabled = volume.enabled;
return true;
}
bool Engine::set_ibl_volume(size_t index, const IBLVolumeD& volume)
{
if (index >= _engine->_iblVolumes.size()) return false;
auto& v = _engine->_iblVolumes[index];
v.center_world = WorldVec3(volume.center);
v.halfExtents = volume.halfExtents; v.halfExtents = volume.halfExtents;
v.paths = to_internal_ibl_paths(volume.paths); v.paths = to_internal_ibl_paths(volume.paths);
v.enabled = volume.enabled; v.enabled = volume.enabled;
@@ -332,6 +387,28 @@ bool Engine::add_gltf_instance(const std::string& name,
return _engine->addGLTFInstance(name, modelPath, transform.to_matrix(), preloadTextures); return _engine->addGLTFInstance(name, modelPath, transform.to_matrix(), preloadTextures);
} }
bool Engine::add_gltf_instance(const std::string& name,
const std::string& modelPath,
const TransformD& transform,
bool preloadTextures)
{
if (!_engine || !_engine->_sceneManager)
{
return false;
}
// Add the instance first (GPU resources), then apply the authoritative world transform in double.
if (!_engine->addGLTFInstance(name, modelPath, glm::mat4(1.0f), preloadTextures))
{
return false;
}
return _engine->_sceneManager->setGLTFInstanceTRSWorld(name,
WorldVec3(transform.position),
transform.rotation,
transform.scale);
}
uint32_t Engine::add_gltf_instance_async(const std::string& name, uint32_t Engine::add_gltf_instance_async(const std::string& name,
const std::string& modelPath, const std::string& modelPath,
const Transform& transform, const Transform& transform,
@@ -340,6 +417,19 @@ uint32_t Engine::add_gltf_instance_async(const std::string& name,
return _engine->loadGLTFAsync(name, modelPath, transform.to_matrix(), preloadTextures); return _engine->loadGLTFAsync(name, modelPath, transform.to_matrix(), preloadTextures);
} }
uint32_t Engine::add_gltf_instance_async(const std::string& name,
const std::string& modelPath,
const TransformD& transform,
bool preloadTextures)
{
return _engine->loadGLTFAsync(name,
modelPath,
WorldVec3(transform.position),
transform.rotation,
transform.scale,
preloadTextures);
}
bool Engine::remove_gltf_instance(const std::string& name) bool Engine::remove_gltf_instance(const std::string& name)
{ {
return _engine->_sceneManager ? _engine->_sceneManager->removeGLTFInstance(name) : false; return _engine->_sceneManager ? _engine->_sceneManager->removeGLTFInstance(name) : false;
@@ -358,6 +448,23 @@ bool Engine::get_gltf_instance_transform(const std::string& name, Transform& out
return false; return false;
} }
bool Engine::get_gltf_instance_transform(const std::string& name, TransformD& out) const
{
if (!_engine->_sceneManager) return false;
WorldVec3 t{};
glm::quat r{};
glm::vec3 s{};
if (_engine->_sceneManager->getGLTFInstanceTRSWorld(name, t, r, s))
{
out.position = glm::dvec3(t);
out.rotation = r;
out.scale = s;
return true;
}
return false;
}
bool Engine::set_gltf_instance_transform(const std::string& name, const Transform& transform) bool Engine::set_gltf_instance_transform(const std::string& name, const Transform& transform)
{ {
return _engine->_sceneManager return _engine->_sceneManager
@@ -365,6 +472,16 @@ bool Engine::set_gltf_instance_transform(const std::string& name, const Transfor
: false; : false;
} }
bool Engine::set_gltf_instance_transform(const std::string& name, const TransformD& transform)
{
return _engine->_sceneManager
? _engine->_sceneManager->setGLTFInstanceTRSWorld(name,
WorldVec3(transform.position),
transform.rotation,
transform.scale)
: false;
}
bool Engine::add_primitive_instance(const std::string& name, bool Engine::add_primitive_instance(const std::string& name,
PrimitiveType type, PrimitiveType type,
const Transform& transform) const Transform& transform)
@@ -382,6 +499,32 @@ bool Engine::add_primitive_instance(const std::string& name,
return _engine->addPrimitiveInstance(name, geomType, transform.to_matrix()); return _engine->addPrimitiveInstance(name, geomType, transform.to_matrix());
} }
bool Engine::add_primitive_instance(const std::string& name,
PrimitiveType type,
const TransformD& transform)
{
AssetManager::MeshGeometryDesc::Type geomType;
switch (type)
{
case PrimitiveType::Cube: geomType = AssetManager::MeshGeometryDesc::Type::Cube; break;
case PrimitiveType::Sphere: geomType = AssetManager::MeshGeometryDesc::Type::Sphere; break;
case PrimitiveType::Plane: geomType = AssetManager::MeshGeometryDesc::Type::Plane; break;
case PrimitiveType::Capsule: geomType = AssetManager::MeshGeometryDesc::Type::Capsule; break;
default: return false;
}
if (!_engine->addPrimitiveInstance(name, geomType, glm::mat4(1.0f)))
{
return false;
}
return _engine->_sceneManager
? _engine->_sceneManager->setMeshInstanceTRSWorld(name,
WorldVec3(transform.position),
transform.rotation,
transform.scale)
: false;
}
bool Engine::remove_mesh_instance(const std::string& name) bool Engine::remove_mesh_instance(const std::string& name)
{ {
return _engine->_sceneManager ? _engine->_sceneManager->removeMeshInstance(name) : false; return _engine->_sceneManager ? _engine->_sceneManager->removeMeshInstance(name) : false;
@@ -400,6 +543,23 @@ bool Engine::get_mesh_instance_transform(const std::string& name, Transform& out
return false; return false;
} }
bool Engine::get_mesh_instance_transform(const std::string& name, TransformD& out) const
{
if (!_engine->_sceneManager) return false;
WorldVec3 t{};
glm::quat r{};
glm::vec3 s{};
if (_engine->_sceneManager->getMeshInstanceTRSWorld(name, t, r, s))
{
out.position = glm::dvec3(t);
out.rotation = r;
out.scale = s;
return true;
}
return false;
}
bool Engine::set_mesh_instance_transform(const std::string& name, const Transform& transform) bool Engine::set_mesh_instance_transform(const std::string& name, const Transform& transform)
{ {
return _engine->_sceneManager return _engine->_sceneManager
@@ -407,6 +567,16 @@ bool Engine::set_mesh_instance_transform(const std::string& name, const Transfor
: false; : false;
} }
bool Engine::set_mesh_instance_transform(const std::string& name, const TransformD& transform)
{
return _engine->_sceneManager
? _engine->_sceneManager->setMeshInstanceTRSWorld(name,
WorldVec3(transform.position),
transform.rotation,
transform.scale)
: false;
}
void Engine::preload_instance_textures(const std::string& name) void Engine::preload_instance_textures(const std::string& name)
{ {
_engine->preloadInstanceTextures(name); _engine->preloadInstanceTextures(name);
@@ -477,7 +647,22 @@ size_t Engine::add_point_light(const PointLight& light)
if (!_engine->_sceneManager) return 0; if (!_engine->_sceneManager) return 0;
SceneManager::PointLight pl; SceneManager::PointLight pl;
pl.position = light.position; pl.position_world = WorldVec3(light.position);
pl.radius = light.radius;
pl.color = light.color;
pl.intensity = light.intensity;
size_t idx = _engine->_sceneManager->getPointLightCount();
_engine->_sceneManager->addPointLight(pl);
return idx;
}
size_t Engine::add_point_light(const PointLightD& light)
{
if (!_engine->_sceneManager) return 0;
SceneManager::PointLight pl;
pl.position_world = WorldVec3(light.position);
pl.radius = light.radius; pl.radius = light.radius;
pl.color = light.color; pl.color = light.color;
pl.intensity = light.intensity; pl.intensity = light.intensity;
@@ -499,7 +684,23 @@ bool Engine::get_point_light(size_t index, PointLight& out) const
SceneManager::PointLight pl; SceneManager::PointLight pl;
if (_engine->_sceneManager->getPointLight(index, pl)) if (_engine->_sceneManager->getPointLight(index, pl))
{ {
out.position = pl.position; out.position = glm::vec3(pl.position_world);
out.radius = pl.radius;
out.color = pl.color;
out.intensity = pl.intensity;
return true;
}
return false;
}
bool Engine::get_point_light(size_t index, PointLightD& out) const
{
if (!_engine->_sceneManager) return false;
SceneManager::PointLight pl;
if (_engine->_sceneManager->getPointLight(index, pl))
{
out.position = pl.position_world;
out.radius = pl.radius; out.radius = pl.radius;
out.color = pl.color; out.color = pl.color;
out.intensity = pl.intensity; out.intensity = pl.intensity;
@@ -513,7 +714,20 @@ bool Engine::set_point_light(size_t index, const PointLight& light)
if (!_engine->_sceneManager) return false; if (!_engine->_sceneManager) return false;
SceneManager::PointLight pl; SceneManager::PointLight pl;
pl.position = light.position; pl.position_world = WorldVec3(light.position);
pl.radius = light.radius;
pl.color = light.color;
pl.intensity = light.intensity;
return _engine->_sceneManager->setPointLight(index, pl);
}
bool Engine::set_point_light(size_t index, const PointLightD& light)
{
if (!_engine->_sceneManager) return false;
SceneManager::PointLight pl;
pl.position_world = WorldVec3(light.position);
pl.radius = light.radius; pl.radius = light.radius;
pl.color = light.color; pl.color = light.color;
pl.intensity = light.intensity; pl.intensity = light.intensity;
@@ -748,7 +962,7 @@ void Engine::set_camera_position(const glm::vec3& position)
{ {
if (_engine->_sceneManager) if (_engine->_sceneManager)
{ {
_engine->_sceneManager->getMainCamera().position = position; _engine->_sceneManager->getMainCamera().position_world = WorldVec3(position);
} }
} }
@@ -756,11 +970,28 @@ glm::vec3 Engine::get_camera_position() const
{ {
if (_engine->_sceneManager) if (_engine->_sceneManager)
{ {
return _engine->_sceneManager->getMainCamera().position; return glm::vec3(_engine->_sceneManager->getMainCamera().position_world);
} }
return glm::vec3(0.0f); return glm::vec3(0.0f);
} }
void Engine::set_camera_position(const glm::dvec3& position)
{
if (_engine->_sceneManager)
{
_engine->_sceneManager->getMainCamera().position_world = position;
}
}
glm::dvec3 Engine::get_camera_position_d() const
{
if (_engine->_sceneManager)
{
return _engine->_sceneManager->getMainCamera().position_world;
}
return glm::dvec3(0.0);
}
void Engine::set_camera_rotation(float pitch, float yaw) void Engine::set_camera_rotation(float pitch, float yaw)
{ {
if (_engine->_sceneManager) if (_engine->_sceneManager)
@@ -821,7 +1052,7 @@ void Engine::camera_look_at(const glm::vec3& target)
if (!_engine->_sceneManager) return; if (!_engine->_sceneManager) return;
Camera& cam = _engine->_sceneManager->getMainCamera(); Camera& cam = _engine->_sceneManager->getMainCamera();
glm::vec3 dir = glm::normalize(target - cam.position); glm::vec3 dir = glm::normalize(target - glm::vec3(cam.position_world));
// For a -Z forward convention, build a quaternion that rotates -Z into dir. // For a -Z forward convention, build a quaternion that rotates -Z into dir.
// Use glm's lookAt-style helper via matrices, then convert to a quaternion. // Use glm's lookAt-style helper via matrices, then convert to a quaternion.
@@ -843,6 +1074,33 @@ void Engine::camera_look_at(const glm::vec3& target)
cam.orientation = glm::quat_cast(rot); cam.orientation = glm::quat_cast(rot);
} }
void Engine::camera_look_at(const glm::dvec3& target)
{
if (!_engine->_sceneManager) return;
Camera& cam = _engine->_sceneManager->getMainCamera();
glm::dvec3 dirD = glm::normalize(target - cam.position_world);
glm::vec3 dir = glm::normalize(glm::vec3(dirD));
// For a -Z forward convention, build a quaternion that rotates -Z into dir.
glm::vec3 up(0.0f, 1.0f, 0.0f);
if (glm::length2(glm::cross(dir, up)) < 1e-6f)
{
up = glm::vec3(0.0f, 0.0f, 1.0f);
}
glm::vec3 f = dir;
glm::vec3 r = glm::normalize(glm::cross(up, f));
glm::vec3 u = glm::cross(f, r);
glm::mat3 rot;
rot[0] = r;
rot[1] = u;
rot[2] = -f; // -Z forward
cam.orientation = glm::quat_cast(rot);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Rendering // Rendering
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -904,6 +1162,15 @@ Engine::PickResult Engine::get_last_pick() const
PickResult r; PickResult r;
r.valid = _engine->_lastPick.valid; r.valid = _engine->_lastPick.valid;
r.ownerName = _engine->_lastPick.ownerName; r.ownerName = _engine->_lastPick.ownerName;
r.worldPosition = glm::vec3(_engine->_lastPick.worldPos);
return r;
}
Engine::PickResultD Engine::get_last_pick_d() const
{
PickResultD r;
r.valid = _engine->_lastPick.valid;
r.ownerName = _engine->_lastPick.ownerName;
r.worldPosition = _engine->_lastPick.worldPos; r.worldPosition = _engine->_lastPick.worldPos;
return r; return r;
} }

View File

@@ -60,6 +60,15 @@ struct PointLight
float intensity{1.0f}; float intensity{1.0f};
}; };
// Double-precision world-space point light data (position only).
struct PointLightD
{
glm::dvec3 position{0.0};
float radius{10.0f};
glm::vec3 color{1.0f};
float intensity{1.0f};
};
// IBL (Image-Based Lighting) paths // IBL (Image-Based Lighting) paths
struct IBLPaths struct IBLPaths
{ {
@@ -78,6 +87,15 @@ struct IBLVolume
bool enabled{true}; bool enabled{true};
}; };
// Double-precision world-space IBL volume (center only).
struct IBLVolumeD
{
glm::dvec3 center{0.0};
glm::vec3 halfExtents{10.0f};
IBLPaths paths;
bool enabled{true};
};
// Transform decomposition // Transform decomposition
struct Transform struct Transform
{ {
@@ -89,6 +107,17 @@ struct Transform
static Transform from_matrix(const glm::mat4& m); static Transform from_matrix(const glm::mat4& m);
}; };
// Double-precision world-space transform (position only).
struct TransformD
{
glm::dvec3 position{0.0};
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f};
glm::mat4 to_matrix() const;
static TransformD from_matrix(const glm::mat4& m);
};
// Engine statistics (read-only) // Engine statistics (read-only)
struct Stats struct Stats
{ {
@@ -174,6 +203,7 @@ public:
// Add a local IBL volume (returns volume index) // Add a local IBL volume (returns volume index)
size_t add_ibl_volume(const IBLVolume& volume); size_t add_ibl_volume(const IBLVolume& volume);
size_t add_ibl_volume(const IBLVolumeD& volume);
// Remove IBL volume by index // Remove IBL volume by index
bool remove_ibl_volume(size_t index); bool remove_ibl_volume(size_t index);
@@ -181,6 +211,8 @@ public:
// Get/set IBL volume properties // Get/set IBL volume properties
bool get_ibl_volume(size_t index, IBLVolume& out) const; bool get_ibl_volume(size_t index, IBLVolume& out) const;
bool set_ibl_volume(size_t index, const IBLVolume& volume); bool set_ibl_volume(size_t index, const IBLVolume& volume);
bool get_ibl_volume(size_t index, IBLVolumeD& out) const;
bool set_ibl_volume(size_t index, const IBLVolumeD& volume);
// Get current active IBL volume index (-1 = global) // Get current active IBL volume index (-1 = global)
int get_active_ibl_volume() const; int get_active_ibl_volume() const;
@@ -200,12 +232,20 @@ public:
const std::string& modelPath, const std::string& modelPath,
const Transform& transform = {}, const Transform& transform = {},
bool preloadTextures = true); bool preloadTextures = true);
bool add_gltf_instance(const std::string& name,
const std::string& modelPath,
const TransformD& transform,
bool preloadTextures = true);
// Add glTF model asynchronously (returns job ID, 0 on failure) // Add glTF model asynchronously (returns job ID, 0 on failure)
uint32_t add_gltf_instance_async(const std::string& name, uint32_t add_gltf_instance_async(const std::string& name,
const std::string& modelPath, const std::string& modelPath,
const Transform& transform = {}, const Transform& transform = {},
bool preloadTextures = true); bool preloadTextures = true);
uint32_t add_gltf_instance_async(const std::string& name,
const std::string& modelPath,
const TransformD& transform,
bool preloadTextures = true);
// Remove glTF instance // Remove glTF instance
bool remove_gltf_instance(const std::string& name); bool remove_gltf_instance(const std::string& name);
@@ -213,11 +253,16 @@ public:
// Get/set glTF instance transform // Get/set glTF instance transform
bool get_gltf_instance_transform(const std::string& name, Transform& out) const; bool get_gltf_instance_transform(const std::string& name, Transform& out) const;
bool set_gltf_instance_transform(const std::string& name, const Transform& transform); bool set_gltf_instance_transform(const std::string& name, const Transform& transform);
bool get_gltf_instance_transform(const std::string& name, TransformD& out) const;
bool set_gltf_instance_transform(const std::string& name, const TransformD& transform);
// Add primitive mesh instance // Add primitive mesh instance
bool add_primitive_instance(const std::string& name, bool add_primitive_instance(const std::string& name,
PrimitiveType type, PrimitiveType type,
const Transform& transform = {}); const Transform& transform = {});
bool add_primitive_instance(const std::string& name,
PrimitiveType type,
const TransformD& transform);
// Remove mesh instance (primitives or custom meshes) // Remove mesh instance (primitives or custom meshes)
bool remove_mesh_instance(const std::string& name); bool remove_mesh_instance(const std::string& name);
@@ -225,6 +270,8 @@ public:
// Get/set mesh instance transform // Get/set mesh instance transform
bool get_mesh_instance_transform(const std::string& name, Transform& out) const; bool get_mesh_instance_transform(const std::string& name, Transform& out) const;
bool set_mesh_instance_transform(const std::string& name, const Transform& transform); bool set_mesh_instance_transform(const std::string& name, const Transform& transform);
bool get_mesh_instance_transform(const std::string& name, TransformD& out) const;
bool set_mesh_instance_transform(const std::string& name, const TransformD& transform);
// Preload textures for an instance (useful before it becomes visible) // Preload textures for an instance (useful before it becomes visible)
void preload_instance_textures(const std::string& name); void preload_instance_textures(const std::string& name);
@@ -256,6 +303,7 @@ public:
// Add point light (returns index) // Add point light (returns index)
size_t add_point_light(const PointLight& light); size_t add_point_light(const PointLight& light);
size_t add_point_light(const PointLightD& light);
// Remove point light by index // Remove point light by index
bool remove_point_light(size_t index); bool remove_point_light(size_t index);
@@ -263,6 +311,8 @@ public:
// Get/set point light properties // Get/set point light properties
bool get_point_light(size_t index, PointLight& out) const; bool get_point_light(size_t index, PointLight& out) const;
bool set_point_light(size_t index, const PointLight& light); bool set_point_light(size_t index, const PointLight& light);
bool get_point_light(size_t index, PointLightD& out) const;
bool set_point_light(size_t index, const PointLightD& light);
// Get point light count // Get point light count
size_t get_point_light_count() const; size_t get_point_light_count() const;
@@ -322,6 +372,8 @@ public:
void set_camera_position(const glm::vec3& position); void set_camera_position(const glm::vec3& position);
glm::vec3 get_camera_position() const; glm::vec3 get_camera_position() const;
void set_camera_position(const glm::dvec3& position);
glm::dvec3 get_camera_position_d() const;
void set_camera_rotation(float pitch, float yaw); void set_camera_rotation(float pitch, float yaw);
void get_camera_rotation(float& pitch, float& yaw) const; void get_camera_rotation(float& pitch, float& yaw) const;
@@ -331,6 +383,7 @@ public:
// Look at a target position // Look at a target position
void camera_look_at(const glm::vec3& target); void camera_look_at(const glm::vec3& target);
void camera_look_at(const glm::dvec3& target);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Rendering // Rendering
@@ -363,8 +416,16 @@ public:
glm::vec3 worldPosition{0.0f}; glm::vec3 worldPosition{0.0f};
}; };
struct PickResultD
{
bool valid{false};
std::string ownerName;
glm::dvec3 worldPosition{0.0};
};
// Get last click selection result // Get last click selection result
PickResult get_last_pick() const; PickResult get_last_pick() const;
PickResultD get_last_pick_d() const;
// Enable/disable ID buffer picking (vs CPU raycast) // Enable/disable ID buffer picking (vs CPU raycast)
void set_use_id_buffer_picking(bool use); void set_use_id_buffer_picking(bool use);

View File

@@ -12,7 +12,16 @@
#include <deque> #include <deque>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#if __has_include(<vulkan/vk_enum_string_helper.h>)
#include <vulkan/vk_enum_string_helper.h> #include <vulkan/vk_enum_string_helper.h>
#elif __has_include(<vk_enum_string_helper.h>)
#include <vk_enum_string_helper.h>
#else
// Fallback for environments without Vulkan-Hpp's enum string helpers.
// This keeps the build working; enum names may not be available.
inline const char *string_VkResult(VkResult) { return "VkResult"; }
inline const char *string_VkFormat(VkFormat) { return "VkFormat"; }
#endif
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>

36
src/core/world.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <glm/vec3.hpp>
#include <cmath>
// Authoritative world-space coordinates are stored as double precision.
using WorldVec3 = glm::dvec3;
inline glm::vec3 world_to_local(const WorldVec3 &world, const WorldVec3 &origin_world)
{
const WorldVec3 local_d = world - origin_world;
return glm::vec3(static_cast<float>(local_d.x),
static_cast<float>(local_d.y),
static_cast<float>(local_d.z));
}
inline WorldVec3 local_to_world(const glm::vec3 &local, const WorldVec3 &origin_world)
{
return origin_world + WorldVec3(local);
}
inline WorldVec3 snap_world(const WorldVec3 &p, double grid_size)
{
if (grid_size <= 0.0)
{
return p;
}
auto snap = [grid_size](double v) {
return std::round(v / grid_size) * grid_size;
};
return WorldVec3(snap(p.x), snap(p.y), snap(p.z));
}

View File

@@ -36,7 +36,6 @@ public:
RGBufferHandle create_buffer(const RGBufferDesc& desc); RGBufferHandle create_buffer(const RGBufferDesc& desc);
// Pass builder API // Pass builder API
struct Pass; // fwd
using RecordCallback = std::function<void(VkCommandBuffer cmd, const class RGPassResources& res, EngineContext* ctx)>; using RecordCallback = std::function<void(VkCommandBuffer cmd, const class RGPassResources& res, EngineContext* ctx)>;
using BuildCallback = std::function<void(class RGPassBuilder& b, EngineContext* ctx)>; using BuildCallback = std::function<void(class RGPassBuilder& b, EngineContext* ctx)>;

View File

@@ -8,7 +8,8 @@
void Camera::update() void Camera::update()
{ {
glm::mat4 cameraRotation = getRotationMatrix(); glm::mat4 cameraRotation = getRotationMatrix();
position += glm::vec3(cameraRotation * glm::vec4(velocity * moveSpeed, 0.f)); glm::vec3 delta = glm::vec3(cameraRotation * glm::vec4(velocity * moveSpeed, 0.f));
position_world += glm::dvec3(delta);
} }
void Camera::processSDLEvent(SDL_Event& e) void Camera::processSDLEvent(SDL_Event& e)
@@ -71,17 +72,17 @@ void Camera::processSDLEvent(SDL_Event& e)
} }
} }
glm::mat4 Camera::getViewMatrix() glm::mat4 Camera::getViewMatrix(const glm::vec3 &position_local) const
{ {
// to create a correct model view, we need to move the world in opposite // to create a correct model view, we need to move the world in opposite
// direction to the camera // direction to the camera
// so we will create the camera model matrix and invert // so we will create the camera model matrix and invert
glm::mat4 cameraTranslation = glm::translate(glm::mat4(1.f), position); glm::mat4 cameraTranslation = glm::translate(glm::mat4(1.f), position_local);
glm::mat4 cameraRotation = getRotationMatrix(); glm::mat4 cameraRotation = getRotationMatrix();
return glm::inverse(cameraTranslation * cameraRotation); return glm::inverse(cameraTranslation * cameraRotation);
} }
glm::mat4 Camera::getRotationMatrix() glm::mat4 Camera::getRotationMatrix() const
{ {
// Use the stored quaternion orientation directly. // Use the stored quaternion orientation directly.
return glm::toMat4(orientation); return glm::toMat4(orientation);

View File

@@ -7,8 +7,8 @@
class Camera { class Camera {
public: public:
glm::vec3 velocity; glm::vec3 velocity{0.0f, 0.0f, 0.0f};
glm::vec3 position; glm::dvec3 position_world{0.0, 0.0, 0.0};
// Orientation stored as a quaternion (local -> world). // Orientation stored as a quaternion (local -> world).
glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f }; glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f };
@@ -20,8 +20,8 @@ public:
// Field of view in degrees for projection // Field of view in degrees for projection
float fovDegrees { 50.f }; float fovDegrees { 50.f };
glm::mat4 getViewMatrix(); glm::mat4 getViewMatrix(const glm::vec3 &position_local) const;
glm::mat4 getRotationMatrix(); glm::mat4 getRotationMatrix() const;
void processSDLEvent(SDL_Event& e); void processSDLEvent(SDL_Event& e);

View File

@@ -72,7 +72,7 @@ void SceneManager::init(EngineContext *context)
_context = context; _context = context;
mainCamera.velocity = glm::vec3(0.f); mainCamera.velocity = glm::vec3(0.f);
mainCamera.position = glm::vec3(30.f, -00.f, 85.f); mainCamera.position_world = WorldVec3(30.0, 0.0, 85.0);
mainCamera.orientation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); mainCamera.orientation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
sceneData.ambientColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f); sceneData.ambientColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f);
@@ -81,18 +81,20 @@ void SceneManager::init(EngineContext *context)
// Seed a couple of default point lights for quick testing. // Seed a couple of default point lights for quick testing.
PointLight warmKey{}; PointLight warmKey{};
warmKey.position = glm::vec3(0.0f, 0.0f, 0.0f); warmKey.position_world = WorldVec3(0.0, 0.0, 0.0);
warmKey.radius = 25.0f; warmKey.radius = 25.0f;
warmKey.color = glm::vec3(1.0f, 0.95f, 0.8f); warmKey.color = glm::vec3(1.0f, 0.95f, 0.8f);
warmKey.intensity = 15.0f; warmKey.intensity = 15.0f;
addPointLight(warmKey); addPointLight(warmKey);
PointLight coolFill{}; PointLight coolFill{};
coolFill.position = glm::vec3(-10.0f, 4.0f, 10.0f); coolFill.position_world = WorldVec3(-10.0, 4.0, 10.0);
coolFill.radius = 20.0f; coolFill.radius = 20.0f;
coolFill.color = glm::vec3(0.6f, 0.7f, 1.0f); coolFill.color = glm::vec3(0.6f, 0.7f, 1.0f);
coolFill.intensity = 10.0f; coolFill.intensity = 10.0f;
addPointLight(coolFill); addPointLight(coolFill);
_camera_position_local = world_to_local(mainCamera.position_world, _origin_world);
} }
void SceneManager::update_scene() void SceneManager::update_scene()
@@ -126,6 +128,24 @@ void SceneManager::update_scene()
mainCamera.update(); mainCamera.update();
// Floating origin: keep render-local coordinates near (0,0,0) by shifting the origin
// when the camera drifts too far in world space.
if (_floating_origin_recenter_threshold > 0.0)
{
const WorldVec3 d = mainCamera.position_world - _origin_world;
const double threshold2 = _floating_origin_recenter_threshold * _floating_origin_recenter_threshold;
if (glm::length2(d) > threshold2)
{
const WorldVec3 newOrigin =
(_floating_origin_snap_size > 0.0)
? snap_world(mainCamera.position_world, _floating_origin_snap_size)
: mainCamera.position_world;
_origin_world = newOrigin;
}
}
_camera_position_local = world_to_local(mainCamera.position_world, _origin_world);
// Simple per-frame dt (seconds) for animations // Simple per-frame dt (seconds) for animations
static auto lastFrameTime = std::chrono::steady_clock::now(); static auto lastFrameTime = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now();
@@ -155,6 +175,11 @@ void SceneManager::update_scene()
} }
}; };
// Root transform to shift "world space" float scenes into render-local space.
// Any object that is authored in world coordinates (float) should be offset by -origin.
const glm::mat4 world_to_local_root =
glm::translate(glm::mat4{1.f}, world_to_local(WorldVec3(0.0, 0.0, 0.0), _origin_world));
// Draw all loaded GLTF scenes (static world), advancing their independent animation states. // Draw all loaded GLTF scenes (static world), advancing their independent animation states.
for (auto &[name, scene] : loadedScenes) for (auto &[name, scene] : loadedScenes)
{ {
@@ -173,7 +198,7 @@ void SceneManager::update_scene()
const size_t opaqueStart = mainDrawContext.OpaqueSurfaces.size(); const size_t opaqueStart = mainDrawContext.OpaqueSurfaces.size();
const size_t transpStart = mainDrawContext.TransparentSurfaces.size(); const size_t transpStart = mainDrawContext.TransparentSurfaces.size();
mainDrawContext.gltfNodeLocalOverrides = nullptr; mainDrawContext.gltfNodeLocalOverrides = nullptr;
scene->Draw(glm::mat4{1.f}, mainDrawContext); scene->Draw(world_to_local_root, mainDrawContext);
mainDrawContext.gltfNodeLocalOverrides = nullptr; mainDrawContext.gltfNodeLocalOverrides = nullptr;
tagOwner(RenderObject::OwnerType::StaticGLTF, name, opaqueStart, transpStart); tagOwner(RenderObject::OwnerType::StaticGLTF, name, opaqueStart, transpStart);
} }
@@ -203,7 +228,8 @@ void SceneManager::update_scene()
{ {
mainDrawContext.gltfNodeLocalOverrides = nullptr; mainDrawContext.gltfNodeLocalOverrides = nullptr;
} }
glm::mat4 instanceTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); glm::vec3 tLocal = world_to_local(inst.translation_world, _origin_world);
glm::mat4 instanceTransform = make_trs_matrix(tLocal, inst.rotation, inst.scale);
inst.scene->Draw(instanceTransform, mainDrawContext); inst.scene->Draw(instanceTransform, mainDrawContext);
mainDrawContext.gltfNodeLocalOverrides = nullptr; mainDrawContext.gltfNodeLocalOverrides = nullptr;
tagOwner(RenderObject::OwnerType::GLTFInstance, kv.first, opaqueStart, transpStart); tagOwner(RenderObject::OwnerType::GLTFInstance, kv.first, opaqueStart, transpStart);
@@ -216,7 +242,8 @@ void SceneManager::update_scene()
{ {
const MeshInstance &inst = kv.second; const MeshInstance &inst = kv.second;
if (!inst.mesh || inst.mesh->surfaces.empty()) continue; if (!inst.mesh || inst.mesh->surfaces.empty()) continue;
glm::mat4 instanceTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); glm::vec3 tLocal = world_to_local(inst.translation_world, _origin_world);
glm::mat4 instanceTransform = make_trs_matrix(tLocal, inst.rotation, inst.scale);
uint32_t surfaceIndex = 0; uint32_t surfaceIndex = 0;
for (const auto &surf: inst.mesh->surfaces) for (const auto &surf: inst.mesh->surfaces)
{ {
@@ -249,7 +276,7 @@ void SceneManager::update_scene()
} }
} }
glm::mat4 view = mainCamera.getViewMatrix(); glm::mat4 view = mainCamera.getViewMatrix(_camera_position_local);
// Use reversed infinite-Z projection (right-handed, -Z forward) to avoid far-plane clipping // Use reversed infinite-Z projection (right-handed, -Z forward) to avoid far-plane clipping
// on very large scenes. Vulkan clip space is 0..1 (GLM_FORCE_DEPTH_ZERO_TO_ONE) and requires Y flip. // on very large scenes. Vulkan clip space is 0..1 (GLM_FORCE_DEPTH_ZERO_TO_ONE) and requires Y flip.
auto makeReversedInfinitePerspective = [](float fovyRadians, float aspect, float zNear) { auto makeReversedInfinitePerspective = [](float fovyRadians, float aspect, float zNear) {
@@ -374,7 +401,8 @@ void SceneManager::update_scene()
for (uint32_t i = 0; i < lightCount; ++i) for (uint32_t i = 0; i < lightCount; ++i)
{ {
const PointLight &pl = pointLights[i]; const PointLight &pl = pointLights[i];
sceneData.punctualLights[i].position_radius = glm::vec4(pl.position, pl.radius); glm::vec3 posLocal = world_to_local(pl.position_world, _origin_world);
sceneData.punctualLights[i].position_radius = glm::vec4(posLocal, pl.radius);
sceneData.punctualLights[i].color_intensity = glm::vec4(pl.color, pl.intensity); sceneData.punctualLights[i].color_intensity = glm::vec4(pl.color, pl.intensity);
} }
for (uint32_t i = lightCount; i < kMaxPunctualLights; ++i) for (uint32_t i = lightCount; i < kMaxPunctualLights; ++i)
@@ -448,7 +476,9 @@ void SceneManager::addMeshInstance(const std::string &name, std::shared_ptr<Mesh
if (!mesh) return; if (!mesh) return;
MeshInstance inst{}; MeshInstance inst{};
inst.mesh = std::move(mesh); inst.mesh = std::move(mesh);
decompose_trs_matrix(transform, inst.translation, inst.rotation, inst.scale); glm::vec3 t{};
decompose_trs_matrix(transform, t, inst.rotation, inst.scale);
inst.translation_world = WorldVec3(t);
inst.boundsTypeOverride = boundsType; inst.boundsTypeOverride = boundsType;
dynamicMeshInstances[name] = std::move(inst); dynamicMeshInstances[name] = std::move(inst);
} }
@@ -461,7 +491,7 @@ bool SceneManager::getMeshInstanceTransform(const std::string &name, glm::mat4 &
return false; return false;
} }
const MeshInstance &inst = it->second; const MeshInstance &inst = it->second;
outTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); outTransform = make_trs_matrix(glm::vec3(inst.translation_world), inst.rotation, inst.scale);
return true; return true;
} }
@@ -473,7 +503,74 @@ bool SceneManager::setMeshInstanceTransform(const std::string &name, const glm::
return false; return false;
} }
MeshInstance &inst = it->second; MeshInstance &inst = it->second;
decompose_trs_matrix(transform, inst.translation, inst.rotation, inst.scale); glm::vec3 t{};
decompose_trs_matrix(transform, t, inst.rotation, inst.scale);
inst.translation_world = WorldVec3(t);
return true;
}
bool SceneManager::getMeshInstanceTransformLocal(const std::string &name, glm::mat4 &outTransformLocal) const
{
auto it = dynamicMeshInstances.find(name);
if (it == dynamicMeshInstances.end())
{
return false;
}
const MeshInstance &inst = it->second;
glm::vec3 tLocal = world_to_local(inst.translation_world, _origin_world);
outTransformLocal = make_trs_matrix(tLocal, inst.rotation, inst.scale);
return true;
}
bool SceneManager::setMeshInstanceTransformLocal(const std::string &name, const glm::mat4 &transformLocal)
{
auto it = dynamicMeshInstances.find(name);
if (it == dynamicMeshInstances.end())
{
return false;
}
MeshInstance &inst = it->second;
glm::vec3 tLocal{};
decompose_trs_matrix(transformLocal, tLocal, inst.rotation, inst.scale);
inst.translation_world = local_to_world(tLocal, _origin_world);
return true;
}
bool SceneManager::getMeshInstanceTRSWorld(const std::string &name,
WorldVec3 &outTranslationWorld,
glm::quat &outRotation,
glm::vec3 &outScale) const
{
auto it = dynamicMeshInstances.find(name);
if (it == dynamicMeshInstances.end())
{
return false;
}
const MeshInstance &inst = it->second;
outTranslationWorld = inst.translation_world;
outRotation = inst.rotation;
outScale = inst.scale;
return true;
}
bool SceneManager::setMeshInstanceTRSWorld(const std::string &name,
const WorldVec3 &translationWorld,
const glm::quat &rotation,
const glm::vec3 &scale)
{
auto it = dynamicMeshInstances.find(name);
if (it == dynamicMeshInstances.end())
{
return false;
}
MeshInstance &inst = it->second;
inst.translation_world = translationWorld;
inst.rotation = rotation;
inst.scale = scale;
return true; return true;
} }
@@ -496,7 +593,9 @@ void SceneManager::addGLTFInstance(const std::string &name, std::shared_ptr<Load
scene->debugName.empty() ? "<unnamed>" : scene->debugName.c_str()); scene->debugName.empty() ? "<unnamed>" : scene->debugName.c_str());
GLTFInstance inst{}; GLTFInstance inst{};
inst.scene = std::move(scene); inst.scene = std::move(scene);
decompose_trs_matrix(transform, inst.translation, inst.rotation, inst.scale); glm::vec3 t{};
decompose_trs_matrix(transform, t, inst.rotation, inst.scale);
inst.translation_world = WorldVec3(t);
if (inst.scene && !inst.scene->animations.empty()) if (inst.scene && !inst.scene->animations.empty())
{ {
inst.animation.activeAnimation = 0; inst.animation.activeAnimation = 0;
@@ -551,7 +650,7 @@ bool SceneManager::getGLTFInstanceTransform(const std::string &name, glm::mat4 &
return false; return false;
} }
const GLTFInstance &inst = it->second; const GLTFInstance &inst = it->second;
outTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); outTransform = make_trs_matrix(glm::vec3(inst.translation_world), inst.rotation, inst.scale);
return true; return true;
} }
@@ -560,7 +659,74 @@ bool SceneManager::setGLTFInstanceTransform(const std::string &name, const glm::
auto it = dynamicGLTFInstances.find(name); auto it = dynamicGLTFInstances.find(name);
if (it == dynamicGLTFInstances.end()) return false; if (it == dynamicGLTFInstances.end()) return false;
GLTFInstance &inst = it->second; GLTFInstance &inst = it->second;
decompose_trs_matrix(transform, inst.translation, inst.rotation, inst.scale); glm::vec3 t{};
decompose_trs_matrix(transform, t, inst.rotation, inst.scale);
inst.translation_world = WorldVec3(t);
return true;
}
bool SceneManager::getGLTFInstanceTransformLocal(const std::string &name, glm::mat4 &outTransformLocal) const
{
auto it = dynamicGLTFInstances.find(name);
if (it == dynamicGLTFInstances.end())
{
return false;
}
const GLTFInstance &inst = it->second;
glm::vec3 tLocal = world_to_local(inst.translation_world, _origin_world);
outTransformLocal = make_trs_matrix(tLocal, inst.rotation, inst.scale);
return true;
}
bool SceneManager::setGLTFInstanceTransformLocal(const std::string &name, const glm::mat4 &transformLocal)
{
auto it = dynamicGLTFInstances.find(name);
if (it == dynamicGLTFInstances.end())
{
return false;
}
GLTFInstance &inst = it->second;
glm::vec3 tLocal{};
decompose_trs_matrix(transformLocal, tLocal, inst.rotation, inst.scale);
inst.translation_world = local_to_world(tLocal, _origin_world);
return true;
}
bool SceneManager::getGLTFInstanceTRSWorld(const std::string &name,
WorldVec3 &outTranslationWorld,
glm::quat &outRotation,
glm::vec3 &outScale) const
{
auto it = dynamicGLTFInstances.find(name);
if (it == dynamicGLTFInstances.end())
{
return false;
}
const GLTFInstance &inst = it->second;
outTranslationWorld = inst.translation_world;
outRotation = inst.rotation;
outScale = inst.scale;
return true;
}
bool SceneManager::setGLTFInstanceTRSWorld(const std::string &name,
const WorldVec3 &translationWorld,
const glm::quat &rotation,
const glm::vec3 &scale)
{
auto it = dynamicGLTFInstances.find(name);
if (it == dynamicGLTFInstances.end())
{
return false;
}
GLTFInstance &inst = it->second;
inst.translation_world = translationWorld;
inst.rotation = rotation;
inst.scale = scale;
return true; return true;
} }

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <core/types.h> #include <core/types.h>
#include <core/world.h>
#include <scene/camera.h> #include <scene/camera.h>
#include <unordered_map> #include <unordered_map>
#include <memory> #include <memory>
@@ -67,10 +68,13 @@ public:
Camera &getMainCamera() { return mainCamera; } Camera &getMainCamera() { return mainCamera; }
WorldVec3 get_world_origin() const { return _origin_world; }
glm::vec3 get_camera_local_position() const { return _camera_position_local; }
// Ray-pick against current DrawContext using per-surface Bounds. // Ray-pick against current DrawContext using per-surface Bounds.
// mousePosPixels is in window coordinates (SDL), origin at top-left. // mousePosPixels is in window coordinates (SDL), origin at top-left.
// Returns true if any object was hit, filling outObject and outWorldPos. // Returns true if any object was hit, filling outObject and outWorldPos.
bool pick(const glm::vec2 &mousePosPixels, RenderObject &outObject, glm::vec3 &outWorldPos); bool pick(const glm::vec2 &mousePosPixels, RenderObject &outObject, WorldVec3 &outWorldPos);
// Resolve an object ID (from ID buffer) back to the RenderObject for // Resolve an object ID (from ID buffer) back to the RenderObject for
// the most recently built DrawContext. Returns false if not found or id==0. // the most recently built DrawContext. Returns false if not found or id==0.
@@ -91,7 +95,7 @@ public:
struct MeshInstance struct MeshInstance
{ {
std::shared_ptr<MeshAsset> mesh; std::shared_ptr<MeshAsset> mesh;
glm::vec3 translation{0.0f, 0.0f, 0.0f}; WorldVec3 translation_world{0.0, 0.0, 0.0};
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f}; glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f, 1.0f, 1.0f}; glm::vec3 scale{1.0f, 1.0f, 1.0f};
std::optional<BoundsType> boundsTypeOverride; std::optional<BoundsType> boundsTypeOverride;
@@ -102,6 +106,10 @@ public:
std::optional<BoundsType> boundsType = {}); std::optional<BoundsType> boundsType = {});
bool getMeshInstanceTransform(const std::string &name, glm::mat4 &outTransform); bool getMeshInstanceTransform(const std::string &name, glm::mat4 &outTransform);
bool setMeshInstanceTransform(const std::string &name, const glm::mat4 &transform); bool setMeshInstanceTransform(const std::string &name, const glm::mat4 &transform);
bool getMeshInstanceTransformLocal(const std::string &name, glm::mat4 &outTransformLocal) const;
bool setMeshInstanceTransformLocal(const std::string &name, const glm::mat4 &transformLocal);
bool getMeshInstanceTRSWorld(const std::string &name, WorldVec3 &outTranslationWorld, glm::quat &outRotation, glm::vec3 &outScale) const;
bool setMeshInstanceTRSWorld(const std::string &name, const WorldVec3 &translationWorld, const glm::quat &rotation, const glm::vec3 &scale);
bool removeMeshInstance(const std::string &name); bool removeMeshInstance(const std::string &name);
void clearMeshInstances(); void clearMeshInstances();
@@ -109,7 +117,7 @@ public:
struct GLTFInstance struct GLTFInstance
{ {
std::shared_ptr<LoadedGLTF> scene; std::shared_ptr<LoadedGLTF> scene;
glm::vec3 translation{0.0f, 0.0f, 0.0f}; WorldVec3 translation_world{0.0, 0.0, 0.0};
glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f}; glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f};
glm::vec3 scale{1.0f, 1.0f, 1.0f}; glm::vec3 scale{1.0f, 1.0f, 1.0f};
LoadedGLTF::AnimationState animation; LoadedGLTF::AnimationState animation;
@@ -123,6 +131,10 @@ public:
bool removeGLTFInstance(const std::string &name); bool removeGLTFInstance(const std::string &name);
bool getGLTFInstanceTransform(const std::string &name, glm::mat4 &outTransform); bool getGLTFInstanceTransform(const std::string &name, glm::mat4 &outTransform);
bool setGLTFInstanceTransform(const std::string &name, const glm::mat4 &transform); bool setGLTFInstanceTransform(const std::string &name, const glm::mat4 &transform);
bool getGLTFInstanceTransformLocal(const std::string &name, glm::mat4 &outTransformLocal) const;
bool setGLTFInstanceTransformLocal(const std::string &name, const glm::mat4 &transformLocal);
bool getGLTFInstanceTRSWorld(const std::string &name, WorldVec3 &outTranslationWorld, glm::quat &outRotation, glm::vec3 &outScale) const;
bool setGLTFInstanceTRSWorld(const std::string &name, const WorldVec3 &translationWorld, const glm::quat &rotation, const glm::vec3 &scale);
void clearGLTFInstances(); void clearGLTFInstances();
// Per-instance glTF node pose overrides (local-space, layered on top of animation/base TRS). // Per-instance glTF node pose overrides (local-space, layered on top of animation/base TRS).
// 'offset' is post-multiplied onto the node's localTransform for this instance only. // 'offset' is post-multiplied onto the node's localTransform for this instance only.
@@ -147,7 +159,7 @@ public:
struct PointLight struct PointLight
{ {
glm::vec3 position; WorldVec3 position_world;
float radius; float radius;
glm::vec3 color; glm::vec3 color;
float intensity; float intensity;
@@ -187,6 +199,10 @@ private:
GPUSceneData sceneData = {}; GPUSceneData sceneData = {};
DrawContext mainDrawContext; DrawContext mainDrawContext;
std::vector<PointLight> pointLights; std::vector<PointLight> pointLights;
WorldVec3 _origin_world{0.0, 0.0, 0.0};
glm::vec3 _camera_position_local{0.0f, 0.0f, 0.0f};
double _floating_origin_recenter_threshold = 1000.0;
double _floating_origin_snap_size = 100.0;
std::unordered_map<std::string, std::shared_ptr<LoadedGLTF> > loadedScenes; std::unordered_map<std::string, std::shared_ptr<LoadedGLTF> > loadedScenes;
// Per-named static glTF scene animation state (independent of instances). // Per-named static glTF scene animation state (independent of instances).

View File

@@ -421,7 +421,7 @@ namespace
} }
} // namespace } // namespace
bool SceneManager::pick(const glm::vec2 &mousePosPixels, RenderObject &outObject, glm::vec3 &outWorldPos) bool SceneManager::pick(const glm::vec2 &mousePosPixels, RenderObject &outObject, WorldVec3 &outWorldPos)
{ {
if (_context == nullptr) if (_context == nullptr)
{ {
@@ -473,7 +473,7 @@ bool SceneManager::pick(const glm::vec2 &mousePosPixels, RenderObject &outObject
-1.0f); -1.0f);
dirCamera = glm::normalize(dirCamera); dirCamera = glm::normalize(dirCamera);
glm::vec3 rayOrigin = mainCamera.position; glm::vec3 rayOrigin = world_to_local(mainCamera.position_world, _origin_world);
glm::mat4 camRotation = mainCamera.getRotationMatrix(); glm::mat4 camRotation = mainCamera.getRotationMatrix();
glm::vec3 rayDir = glm::normalize(glm::vec3(camRotation * glm::vec4(dirCamera, 0.0f))); glm::vec3 rayDir = glm::normalize(glm::vec3(camRotation * glm::vec4(dirCamera, 0.0f)));
@@ -539,7 +539,7 @@ bool SceneManager::pick(const glm::vec2 &mousePosPixels, RenderObject &outObject
if (anyHit) if (anyHit)
{ {
outWorldPos = bestHitPos; outWorldPos = local_to_world(bestHitPos, _origin_world);
} }
return anyHit; return anyHit;