ADD: Floating origin system
This commit is contained in:
@@ -4,6 +4,7 @@ add_executable (vulkan_engine
|
||||
main.cpp
|
||||
# core root
|
||||
core/types.h
|
||||
core/world.h
|
||||
core/config.h
|
||||
core/context.h
|
||||
core/context.cpp
|
||||
|
||||
@@ -104,6 +104,32 @@ AsyncAssetLoader::JobID AsyncAssetLoader::load_gltf_async(const std::string &sce
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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)
|
||||
if (job->preload_textures && _textures && _engine && _engine->_resourceManager)
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
#include <thread>
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include "scene/vk_loader.h"
|
||||
#include "core/world.h"
|
||||
#include "core/assets/texture_cache.h"
|
||||
|
||||
class VulkanEngine;
|
||||
@@ -41,6 +44,13 @@ public:
|
||||
const glm::mat4 &transform,
|
||||
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);
|
||||
|
||||
// Main-thread integration: commit completed jobs into the SceneManager.
|
||||
@@ -66,6 +76,10 @@ private:
|
||||
std::string scene_name;
|
||||
std::string model_relative_path;
|
||||
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};
|
||||
|
||||
std::shared_ptr<LoadedGLTF> scene;
|
||||
|
||||
@@ -533,7 +533,7 @@ void TextureCache::worker_loop()
|
||||
{
|
||||
ktx_size_t off = 0, len = 0;
|
||||
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 h = std::max(1u, baseH >> mip);
|
||||
out.ktx.levels.push_back({ static_cast<uint64_t>(off), static_cast<uint64_t>(len), w, h });
|
||||
|
||||
@@ -587,6 +587,36 @@ uint32_t VulkanEngine::loadGLTFAsync(const std::string &sceneName,
|
||||
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)
|
||||
{
|
||||
if (!_textureCache || !_sceneManager)
|
||||
@@ -746,16 +776,16 @@ void VulkanEngine::draw()
|
||||
// Update IBL based on camera position and user-defined reflection volumes.
|
||||
if (_iblManager && _sceneManager)
|
||||
{
|
||||
glm::vec3 camPos = _sceneManager->getMainCamera().position;
|
||||
WorldVec3 camPosWorld = _sceneManager->getMainCamera().position_world;
|
||||
int newVolume = -1;
|
||||
for (size_t i = 0; i < _iblVolumes.size(); ++i)
|
||||
{
|
||||
const IBLVolume &v = _iblVolumes[i];
|
||||
if (!v.enabled) continue;
|
||||
glm::vec3 local = camPos - v.center;
|
||||
if (std::abs(local.x) <= v.halfExtents.x &&
|
||||
std::abs(local.y) <= v.halfExtents.y &&
|
||||
std::abs(local.z) <= v.halfExtents.z)
|
||||
WorldVec3 local = camPosWorld - v.center_world;
|
||||
if (std::abs(local.x) <= static_cast<double>(v.halfExtents.x) &&
|
||||
std::abs(local.y) <= static_cast<double>(v.halfExtents.y) &&
|
||||
std::abs(local.z) <= static_cast<double>(v.halfExtents.z))
|
||||
{
|
||||
newVolume = static_cast<int>(i);
|
||||
break;
|
||||
@@ -800,7 +830,7 @@ void VulkanEngine::draw()
|
||||
if (_sceneManager && _mousePosPixels.x >= 0.0f && _mousePosPixels.y >= 0.0f)
|
||||
{
|
||||
RenderObject hoverObj{};
|
||||
glm::vec3 hoverPos{};
|
||||
WorldVec3 hoverPos{};
|
||||
if (_sceneManager->pick(_mousePosPixels, hoverObj, hoverPos))
|
||||
{
|
||||
_hoverPick.mesh = hoverObj.sourceMesh;
|
||||
@@ -1225,7 +1255,7 @@ void VulkanEngine::run()
|
||||
if (_sceneManager)
|
||||
{
|
||||
RenderObject hitObject{};
|
||||
glm::vec3 hitPos{};
|
||||
WorldVec3 hitPos{};
|
||||
if (_sceneManager->pick(releasePos, hitObject, hitPos))
|
||||
{
|
||||
_lastPick.mesh = hitObject.sourceMesh;
|
||||
@@ -1269,8 +1299,8 @@ void VulkanEngine::run()
|
||||
info.ownerType = obj.ownerType;
|
||||
info.ownerName = obj.ownerName;
|
||||
// Use bounds origin transformed to world as a representative point.
|
||||
glm::vec3 centerWorld = glm::vec3(obj.transform * glm::vec4(obj.bounds.origin, 1.0f));
|
||||
info.worldPos = centerWorld;
|
||||
glm::vec3 centerLocal = glm::vec3(obj.transform * glm::vec4(obj.bounds.origin, 1.0f));
|
||||
info.worldPos = local_to_world(centerLocal, _sceneManager->get_world_origin());
|
||||
info.worldTransform = obj.transform;
|
||||
info.firstIndex = obj.firstIndex;
|
||||
info.indexCount = obj.indexCount;
|
||||
@@ -1365,7 +1395,8 @@ void VulkanEngine::run()
|
||||
if (_sceneManager->resolveObjectID(pickedID, picked))
|
||||
{
|
||||
// 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.scene = picked.sourceScene;
|
||||
_lastPick.node = picked.sourceNode;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <core/world.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -123,7 +124,7 @@ public:
|
||||
// Simple world-space IBL reflection volumes (axis-aligned boxes).
|
||||
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};
|
||||
IBLPaths paths{}; // HDRI paths for this volume
|
||||
bool enabled{true};
|
||||
@@ -149,7 +150,7 @@ public:
|
||||
Node *node = nullptr;
|
||||
RenderObject::OwnerType ownerType = RenderObject::OwnerType::None;
|
||||
std::string ownerName;
|
||||
glm::vec3 worldPos{0.0f};
|
||||
WorldVec3 worldPos{0.0, 0.0, 0.0};
|
||||
glm::mat4 worldTransform{1.0f};
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t firstIndex = 0;
|
||||
@@ -242,6 +243,13 @@ public:
|
||||
const glm::mat4 &transform = glm::mat4(1.f),
|
||||
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
|
||||
// available before the object becomes visible (visibility-driven loading).
|
||||
void preloadInstanceTextures(const std::string &instanceName);
|
||||
|
||||
@@ -245,7 +245,7 @@ namespace
|
||||
VulkanEngine::IBLVolume vol{};
|
||||
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.paths = eng->_globalIBLPaths;
|
||||
@@ -259,7 +259,13 @@ namespace
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Volume %zu", i);
|
||||
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);
|
||||
|
||||
// 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("Draw fmt: %s", string_VkFormat(eng->_swapchainManager->drawImage().imageFormat));
|
||||
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
|
||||
@@ -956,18 +973,18 @@ namespace
|
||||
SceneManager::PointLight 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};
|
||||
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::ColorEdit3("Color", col);
|
||||
changed |= ImGui::SliderFloat("Intensity", &pl.intensity, 0.0f, 100.0f);
|
||||
|
||||
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]);
|
||||
sceneMgr->setPointLight(static_cast<size_t>(selectedLight), pl);
|
||||
}
|
||||
@@ -983,12 +1000,12 @@ namespace
|
||||
// Controls for adding a new light
|
||||
ImGui::Separator();
|
||||
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 newColor[3] = {1.0f, 1.0f, 1.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::ColorEdit3("New color", newColor);
|
||||
ImGui::SliderFloat("New intensity", &newIntensity, 0.0f, 100.0f);
|
||||
@@ -996,7 +1013,7 @@ namespace
|
||||
if (ImGui::Button("Add point light"))
|
||||
{
|
||||
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.color = glm::vec3(newColor[0], newColor[1], newColor[2]);
|
||||
pl.intensity = newIntensity;
|
||||
@@ -1214,7 +1231,7 @@ namespace
|
||||
|
||||
if (pick->ownerType == RenderObject::OwnerType::MeshInstance)
|
||||
{
|
||||
if (sceneMgr->getMeshInstanceTransform(pick->ownerName, targetTransform))
|
||||
if (sceneMgr->getMeshInstanceTransformLocal(pick->ownerName, targetTransform))
|
||||
{
|
||||
target = GizmoTarget::MeshInstance;
|
||||
ImGui::Text("Editing mesh instance: %s", pick->ownerName.c_str());
|
||||
@@ -1222,7 +1239,7 @@ namespace
|
||||
}
|
||||
else if (pick->ownerType == RenderObject::OwnerType::GLTFInstance)
|
||||
{
|
||||
if (sceneMgr->getGLTFInstanceTransform(pick->ownerName, targetTransform))
|
||||
if (sceneMgr->getGLTFInstanceTransformLocal(pick->ownerName, targetTransform))
|
||||
{
|
||||
target = GizmoTarget::GLTFInstance;
|
||||
ImGui::Text("Editing glTF instance: %s", pick->ownerName.c_str());
|
||||
@@ -1270,8 +1287,8 @@ namespace
|
||||
: 1.0f;
|
||||
|
||||
// Distance from camera to object; clamp to avoid degenerate planes.
|
||||
glm::vec3 camPos = cam.position;
|
||||
glm::vec3 objPos = pick->worldPos;
|
||||
glm::vec3 camPos = sceneMgr->get_camera_local_position();
|
||||
glm::vec3 objPos = glm::vec3(targetTransform[3]);
|
||||
float dist = glm::length(objPos - camPos);
|
||||
if (!std::isfinite(dist) || dist <= 0.0f)
|
||||
{
|
||||
@@ -1282,7 +1299,7 @@ namespace
|
||||
float nearPlane = glm::max(0.05f, dist * 0.05f);
|
||||
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 before = targetTransform;
|
||||
@@ -1313,10 +1330,10 @@ namespace
|
||||
switch (target)
|
||||
{
|
||||
case GizmoTarget::MeshInstance:
|
||||
sceneMgr->setMeshInstanceTransform(pick->ownerName, targetTransform);
|
||||
sceneMgr->setMeshInstanceTransformLocal(pick->ownerName, targetTransform);
|
||||
break;
|
||||
case GizmoTarget::GLTFInstance:
|
||||
sceneMgr->setGLTFInstanceTransform(pick->ownerName, targetTransform);
|
||||
sceneMgr->setGLTFInstanceTransformLocal(pick->ownerName, targetTransform);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -1324,7 +1341,7 @@ namespace
|
||||
|
||||
// Keep pick debug info roughly in sync.
|
||||
pick->worldTransform = targetTransform;
|
||||
pick->worldPos = glm::vec3(targetTransform[3]);
|
||||
pick->worldPos = local_to_world(glm::vec3(targetTransform[3]), sceneMgr->get_world_origin());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -37,6 +37,25 @@ Transform Transform::from_matrix(const glm::mat4& m)
|
||||
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
|
||||
// ============================================================================
|
||||
@@ -254,7 +273,19 @@ void Engine::set_global_ibl_paths(const IBLPaths& paths)
|
||||
size_t Engine::add_ibl_volume(const IBLVolume& volume)
|
||||
{
|
||||
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.paths = to_internal_ibl_paths(volume.paths);
|
||||
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;
|
||||
|
||||
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.paths = from_internal_ibl_paths(v.paths);
|
||||
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;
|
||||
|
||||
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.paths = to_internal_ibl_paths(volume.paths);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
const std::string& modelPath,
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return _engine->_sceneManager
|
||||
@@ -365,6 +472,16 @@ bool Engine::set_gltf_instance_transform(const std::string& name, const Transfor
|
||||
: 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,
|
||||
PrimitiveType type,
|
||||
const Transform& transform)
|
||||
@@ -382,6 +499,32 @@ bool Engine::add_primitive_instance(const std::string& name,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return _engine->_sceneManager
|
||||
@@ -407,6 +567,16 @@ bool Engine::set_mesh_instance_transform(const std::string& name, const Transfor
|
||||
: 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)
|
||||
{
|
||||
_engine->preloadInstanceTextures(name);
|
||||
@@ -477,7 +647,22 @@ size_t Engine::add_point_light(const PointLight& light)
|
||||
if (!_engine->_sceneManager) return 0;
|
||||
|
||||
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.color = light.color;
|
||||
pl.intensity = light.intensity;
|
||||
@@ -499,7 +684,23 @@ bool Engine::get_point_light(size_t index, PointLight& out) const
|
||||
SceneManager::PointLight 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.color = pl.color;
|
||||
out.intensity = pl.intensity;
|
||||
@@ -513,7 +714,20 @@ bool Engine::set_point_light(size_t index, const PointLight& light)
|
||||
if (!_engine->_sceneManager) return false;
|
||||
|
||||
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.color = light.color;
|
||||
pl.intensity = light.intensity;
|
||||
@@ -748,7 +962,7 @@ void Engine::set_camera_position(const glm::vec3& position)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return _engine->_sceneManager->getMainCamera().position;
|
||||
return glm::vec3(_engine->_sceneManager->getMainCamera().position_world);
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (_engine->_sceneManager)
|
||||
@@ -821,7 +1052,7 @@ void Engine::camera_look_at(const glm::vec3& target)
|
||||
if (!_engine->_sceneManager) return;
|
||||
|
||||
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.
|
||||
// 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);
|
||||
}
|
||||
|
||||
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
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -904,6 +1162,15 @@ Engine::PickResult Engine::get_last_pick() const
|
||||
PickResult r;
|
||||
r.valid = _engine->_lastPick.valid;
|
||||
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;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,15 @@ struct PointLight
|
||||
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
|
||||
struct IBLPaths
|
||||
{
|
||||
@@ -78,6 +87,15 @@ struct IBLVolume
|
||||
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
|
||||
struct Transform
|
||||
{
|
||||
@@ -89,6 +107,17 @@ struct Transform
|
||||
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)
|
||||
struct Stats
|
||||
{
|
||||
@@ -174,6 +203,7 @@ public:
|
||||
|
||||
// Add a local IBL volume (returns volume index)
|
||||
size_t add_ibl_volume(const IBLVolume& volume);
|
||||
size_t add_ibl_volume(const IBLVolumeD& volume);
|
||||
|
||||
// Remove IBL volume by index
|
||||
bool remove_ibl_volume(size_t index);
|
||||
@@ -181,6 +211,8 @@ public:
|
||||
// Get/set IBL volume properties
|
||||
bool get_ibl_volume(size_t index, IBLVolume& out) const;
|
||||
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)
|
||||
int get_active_ibl_volume() const;
|
||||
@@ -200,12 +232,20 @@ public:
|
||||
const std::string& modelPath,
|
||||
const Transform& transform = {},
|
||||
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)
|
||||
uint32_t add_gltf_instance_async(const std::string& name,
|
||||
const std::string& modelPath,
|
||||
const Transform& transform = {},
|
||||
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
|
||||
bool remove_gltf_instance(const std::string& name);
|
||||
@@ -213,11 +253,16 @@ public:
|
||||
// Get/set glTF instance transform
|
||||
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 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
|
||||
bool add_primitive_instance(const std::string& name,
|
||||
PrimitiveType type,
|
||||
const Transform& transform = {});
|
||||
bool add_primitive_instance(const std::string& name,
|
||||
PrimitiveType type,
|
||||
const TransformD& transform);
|
||||
|
||||
// Remove mesh instance (primitives or custom meshes)
|
||||
bool remove_mesh_instance(const std::string& name);
|
||||
@@ -225,6 +270,8 @@ public:
|
||||
// Get/set mesh instance transform
|
||||
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 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)
|
||||
void preload_instance_textures(const std::string& name);
|
||||
@@ -256,6 +303,7 @@ public:
|
||||
|
||||
// Add point light (returns index)
|
||||
size_t add_point_light(const PointLight& light);
|
||||
size_t add_point_light(const PointLightD& light);
|
||||
|
||||
// Remove point light by index
|
||||
bool remove_point_light(size_t index);
|
||||
@@ -263,6 +311,8 @@ public:
|
||||
// Get/set point light properties
|
||||
bool get_point_light(size_t index, PointLight& out) const;
|
||||
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
|
||||
size_t get_point_light_count() const;
|
||||
@@ -322,6 +372,8 @@ public:
|
||||
|
||||
void set_camera_position(const glm::vec3& position);
|
||||
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 get_camera_rotation(float& pitch, float& yaw) const;
|
||||
@@ -331,6 +383,7 @@ public:
|
||||
|
||||
// Look at a target position
|
||||
void camera_look_at(const glm::vec3& target);
|
||||
void camera_look_at(const glm::dvec3& target);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Rendering
|
||||
@@ -363,8 +416,16 @@ public:
|
||||
glm::vec3 worldPosition{0.0f};
|
||||
};
|
||||
|
||||
struct PickResultD
|
||||
{
|
||||
bool valid{false};
|
||||
std::string ownerName;
|
||||
glm::dvec3 worldPosition{0.0};
|
||||
};
|
||||
|
||||
// Get last click selection result
|
||||
PickResult get_last_pick() const;
|
||||
PickResultD get_last_pick_d() const;
|
||||
|
||||
// Enable/disable ID buffer picking (vs CPU raycast)
|
||||
void set_use_id_buffer_picking(bool use);
|
||||
|
||||
@@ -12,7 +12,16 @@
|
||||
#include <deque>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#if __has_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>
|
||||
|
||||
|
||||
36
src/core/world.h
Normal file
36
src/core/world.h
Normal 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));
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ public:
|
||||
RGBufferHandle create_buffer(const RGBufferDesc& desc);
|
||||
|
||||
// Pass builder API
|
||||
struct Pass; // fwd
|
||||
using RecordCallback = std::function<void(VkCommandBuffer cmd, const class RGPassResources& res, EngineContext* ctx)>;
|
||||
using BuildCallback = std::function<void(class RGPassBuilder& b, EngineContext* ctx)>;
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
void Camera::update()
|
||||
{
|
||||
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)
|
||||
@@ -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
|
||||
// direction to the camera
|
||||
// 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();
|
||||
return glm::inverse(cameraTranslation * cameraRotation);
|
||||
}
|
||||
|
||||
glm::mat4 Camera::getRotationMatrix()
|
||||
glm::mat4 Camera::getRotationMatrix() const
|
||||
{
|
||||
// Use the stored quaternion orientation directly.
|
||||
return glm::toMat4(orientation);
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
glm::vec3 velocity;
|
||||
glm::vec3 position;
|
||||
glm::vec3 velocity{0.0f, 0.0f, 0.0f};
|
||||
glm::dvec3 position_world{0.0, 0.0, 0.0};
|
||||
// Orientation stored as a quaternion (local -> world).
|
||||
glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
@@ -20,8 +20,8 @@ public:
|
||||
// Field of view in degrees for projection
|
||||
float fovDegrees { 50.f };
|
||||
|
||||
glm::mat4 getViewMatrix();
|
||||
glm::mat4 getRotationMatrix();
|
||||
glm::mat4 getViewMatrix(const glm::vec3 &position_local) const;
|
||||
glm::mat4 getRotationMatrix() const;
|
||||
|
||||
void processSDLEvent(SDL_Event& e);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ void SceneManager::init(EngineContext *context)
|
||||
_context = context;
|
||||
|
||||
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);
|
||||
|
||||
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.
|
||||
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.color = glm::vec3(1.0f, 0.95f, 0.8f);
|
||||
warmKey.intensity = 15.0f;
|
||||
addPointLight(warmKey);
|
||||
|
||||
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.color = glm::vec3(0.6f, 0.7f, 1.0f);
|
||||
coolFill.intensity = 10.0f;
|
||||
addPointLight(coolFill);
|
||||
|
||||
_camera_position_local = world_to_local(mainCamera.position_world, _origin_world);
|
||||
}
|
||||
|
||||
void SceneManager::update_scene()
|
||||
@@ -126,6 +128,24 @@ void SceneManager::update_scene()
|
||||
|
||||
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
|
||||
static auto lastFrameTime = 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.
|
||||
for (auto &[name, scene] : loadedScenes)
|
||||
{
|
||||
@@ -173,7 +198,7 @@ void SceneManager::update_scene()
|
||||
const size_t opaqueStart = mainDrawContext.OpaqueSurfaces.size();
|
||||
const size_t transpStart = mainDrawContext.TransparentSurfaces.size();
|
||||
mainDrawContext.gltfNodeLocalOverrides = nullptr;
|
||||
scene->Draw(glm::mat4{1.f}, mainDrawContext);
|
||||
scene->Draw(world_to_local_root, mainDrawContext);
|
||||
mainDrawContext.gltfNodeLocalOverrides = nullptr;
|
||||
tagOwner(RenderObject::OwnerType::StaticGLTF, name, opaqueStart, transpStart);
|
||||
}
|
||||
@@ -203,7 +228,8 @@ void SceneManager::update_scene()
|
||||
{
|
||||
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);
|
||||
mainDrawContext.gltfNodeLocalOverrides = nullptr;
|
||||
tagOwner(RenderObject::OwnerType::GLTFInstance, kv.first, opaqueStart, transpStart);
|
||||
@@ -216,7 +242,8 @@ void SceneManager::update_scene()
|
||||
{
|
||||
const MeshInstance &inst = kv.second;
|
||||
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;
|
||||
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
|
||||
// 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) {
|
||||
@@ -374,7 +401,8 @@ void SceneManager::update_scene()
|
||||
for (uint32_t i = 0; i < lightCount; ++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);
|
||||
}
|
||||
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;
|
||||
MeshInstance inst{};
|
||||
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;
|
||||
dynamicMeshInstances[name] = std::move(inst);
|
||||
}
|
||||
@@ -461,7 +491,7 @@ bool SceneManager::getMeshInstanceTransform(const std::string &name, glm::mat4 &
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -473,7 +503,74 @@ bool SceneManager::setMeshInstanceTransform(const std::string &name, const glm::
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -496,7 +593,9 @@ void SceneManager::addGLTFInstance(const std::string &name, std::shared_ptr<Load
|
||||
scene->debugName.empty() ? "<unnamed>" : scene->debugName.c_str());
|
||||
GLTFInstance inst{};
|
||||
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())
|
||||
{
|
||||
inst.animation.activeAnimation = 0;
|
||||
@@ -551,7 +650,7 @@ bool SceneManager::getGLTFInstanceTransform(const std::string &name, glm::mat4 &
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -560,7 +659,74 @@ bool SceneManager::setGLTFInstanceTransform(const std::string &name, const glm::
|
||||
auto it = dynamicGLTFInstances.find(name);
|
||||
if (it == dynamicGLTFInstances.end()) return false;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <core/types.h>
|
||||
#include <core/world.h>
|
||||
#include <scene/camera.h>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
@@ -67,10 +68,13 @@ public:
|
||||
|
||||
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.
|
||||
// mousePosPixels is in window coordinates (SDL), origin at top-left.
|
||||
// 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
|
||||
// the most recently built DrawContext. Returns false if not found or id==0.
|
||||
@@ -91,7 +95,7 @@ public:
|
||||
struct MeshInstance
|
||||
{
|
||||
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::vec3 scale{1.0f, 1.0f, 1.0f};
|
||||
std::optional<BoundsType> boundsTypeOverride;
|
||||
@@ -102,6 +106,10 @@ public:
|
||||
std::optional<BoundsType> boundsType = {});
|
||||
bool getMeshInstanceTransform(const std::string &name, glm::mat4 &outTransform);
|
||||
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);
|
||||
void clearMeshInstances();
|
||||
|
||||
@@ -109,7 +117,7 @@ public:
|
||||
struct GLTFInstance
|
||||
{
|
||||
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::vec3 scale{1.0f, 1.0f, 1.0f};
|
||||
LoadedGLTF::AnimationState animation;
|
||||
@@ -123,6 +131,10 @@ public:
|
||||
bool removeGLTFInstance(const std::string &name);
|
||||
bool getGLTFInstanceTransform(const std::string &name, glm::mat4 &outTransform);
|
||||
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();
|
||||
// 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.
|
||||
@@ -147,7 +159,7 @@ public:
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
glm::vec3 position;
|
||||
WorldVec3 position_world;
|
||||
float radius;
|
||||
glm::vec3 color;
|
||||
float intensity;
|
||||
@@ -187,6 +199,10 @@ private:
|
||||
GPUSceneData sceneData = {};
|
||||
DrawContext mainDrawContext;
|
||||
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;
|
||||
// Per-named static glTF scene animation state (independent of instances).
|
||||
|
||||
@@ -421,7 +421,7 @@ 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)
|
||||
{
|
||||
@@ -473,7 +473,7 @@ bool SceneManager::pick(const glm::vec2 &mousePosPixels, RenderObject &outObject
|
||||
-1.0f);
|
||||
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::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)
|
||||
{
|
||||
outWorldPos = bestHitPos;
|
||||
outWorldPos = local_to_world(bestHitPos, _origin_world);
|
||||
}
|
||||
|
||||
return anyHit;
|
||||
|
||||
Reference in New Issue
Block a user