From 534d513be286d2496e3814151400387f1985a696 Mon Sep 17 00:00:00 2001 From: Anemunt <69436164+darkresident55@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:48:44 -0500 Subject: [PATCH] Well, unfinished texture filtering system + Added Better Audio Source Loading and fixed up some stuff within the rendering. --- Scripts/SampleInspector.cpp | 2 +- docs/Scripting.md | 18 +++++ src/AudioSystem.cpp | 61 ++++++++++------ src/AudioSystem.h | 3 +- src/EditorWindows/SceneWindows.cpp | 110 ++++++++++++++++++++++++++++- src/Engine.cpp | 24 +++++++ src/Engine.h | 10 +++ src/PhysicsSystem.cpp | 92 ++++++++++++++++++++++++ src/PhysicsSystem.h | 6 ++ src/Rendering.cpp | 17 +++++ src/Rendering.h | 3 + src/ScriptRuntime.cpp | 37 ++++++++++ src/ScriptRuntime.h | 7 ++ 13 files changed, 366 insertions(+), 24 deletions(-) diff --git a/Scripts/SampleInspector.cpp b/Scripts/SampleInspector.cpp index 1e08b05..cd3259a 100644 --- a/Scripts/SampleInspector.cpp +++ b/Scripts/SampleInspector.cpp @@ -40,7 +40,7 @@ std::string getSetting(const ScriptContext& ctx, const std::string& key, const s void loadSettings(ScriptContext& ctx) { if (!ctx.script || !ctx.object) return; - if (settingsLoadedForId == ctx.object->id && settingsLoadedForScript == ctx.script) return; + if (settingsLoadedForId == ctx.object->id && settingsLoadedForScript == ctx.script) return;Segmentation fault (core dumped) settingsLoadedForId = ctx.object->id; settingsLoadedForScript = ctx.script; diff --git a/docs/Scripting.md b/docs/Scripting.md index 7247e52..f0a7cc6 100644 --- a/docs/Scripting.md +++ b/docs/Scripting.md @@ -23,6 +23,12 @@ Available methods: - `FindObjectByName`, `FindObjectById` - `SetPosition`, `SetRotation`, `SetScale` +- `HasRigidbody` +- `SetRigidbodyVelocity`, `GetRigidbodyVelocity` +- `SetRigidbodyAngularVelocity`, `GetRigidbodyAngularVelocity` +- `AddRigidbodyForce`, `AddRigidbodyImpulse` +- `AddRigidbodyTorque`, `AddRigidbodyAngularImpulse` +- `SetRigidbodyRotation`, `TeleportRigidbody` - `MarkDirty` (flags the project as having unsaved changes) Fields: - `engine`: pointer to the Engine @@ -60,3 +66,15 @@ void Script_TickUpdate(ScriptContext& ctx, float dt) { - Scripts tick for all objects every frame, even if not selected. - Spec/Test toggles are global (main menu → Scripts). - Compile scripts via the UI “Compile Script” button or run the build command; wrapper generation is automatic. + +## Manual compile (CLI) +Linux example: +```bash +g++ -std=c++20 -fPIC -O0 -g -I../src -I../include -c SampleInspector.cpp -o ../Cache/ScriptBin/SampleInspector.o +g++ -shared ../Cache/ScriptBin/SampleInspector.o -o ../Cache/ScriptBin/SampleInspector.so -ldl -lpthread +``` +Windows example: +```bat +cl /nologo /std:c++20 /EHsc /MD /Zi /Od /I ..\\src /I ..\\include /c SampleInspector.cpp /Fo ..\\Cache\\ScriptBin\\SampleInspector.obj +link /nologo /DLL ..\\Cache\\ScriptBin\\SampleInspector.obj /OUT:..\\Cache\\ScriptBin\\SampleInspector.dll User32.lib Advapi32.lib +``` diff --git a/src/AudioSystem.cpp b/src/AudioSystem.cpp index c7ecaf6..652a090 100644 --- a/src/AudioSystem.cpp +++ b/src/AudioSystem.cpp @@ -30,7 +30,9 @@ void AudioSystem::shutdown() { void AudioSystem::destroyActiveSounds() { for (auto& kv : activeSounds) { - ma_sound_uninit(&kv.second.sound); + if (kv.second) { + ma_sound_uninit(&kv.second->sound); + } } activeSounds.clear(); } @@ -42,7 +44,7 @@ void AudioSystem::onPlayStart(const std::vector& objects) { if (!obj.enabled || !obj.hasAudioSource || obj.audioSource.clipPath.empty()) continue; if (!obj.audioSource.enabled) continue; if (ensureSoundFor(obj) && obj.audioSource.playOnStart) { - ma_sound_start(&activeSounds[obj.id].sound); + ma_sound_start(&activeSounds[obj.id]->sound); } } } @@ -54,44 +56,56 @@ void AudioSystem::onPlayStop() { bool AudioSystem::ensureSoundFor(const SceneObject& obj) { auto it = activeSounds.find(obj.id); if (it != activeSounds.end()) { - if (it->second.clipPath == obj.audioSource.clipPath) { - refreshSoundParams(obj, it->second); + if (it->second && it->second->clipPath == obj.audioSource.clipPath) { + refreshSoundParams(obj, *it->second); return true; } - ma_sound_uninit(&it->second.sound); + if (it->second) { + ma_sound_uninit(&it->second->sound); + } activeSounds.erase(it); } + if (!fs::exists(obj.audioSource.clipPath)) { + if (missingClips.insert(obj.audioSource.clipPath).second) { + std::cerr << "AudioSystem: clip not found " << obj.audioSource.clipPath << "\n"; + } + return false; + } + missingClips.erase(obj.audioSource.clipPath); + if (!initialized && !init()) return false; - ActiveSound snd{}; + auto snd = std::make_unique(); ma_result res = ma_sound_init_from_file( &engine, obj.audioSource.clipPath.c_str(), MA_SOUND_FLAG_STREAM, nullptr, nullptr, - &snd.sound + &snd->sound ); if (res != MA_SUCCESS) { std::cerr << "AudioSystem: failed to load " << obj.audioSource.clipPath << " (" << res << ")\n"; return false; } - snd.clipPath = obj.audioSource.clipPath; - snd.spatial = obj.audioSource.spatial; - snd.started = false; - refreshSoundParams(obj, snd); - activeSounds[obj.id] = std::move(snd); + snd->clipPath = obj.audioSource.clipPath; + snd->spatial = obj.audioSource.spatial; + snd->started = false; + refreshSoundParams(obj, *snd); + activeSounds.emplace(obj.id, std::move(snd)); return true; } void AudioSystem::refreshSoundParams(const SceneObject& obj, ActiveSound& snd) { + float minDist = std::max(0.1f, obj.audioSource.minDistance); + float maxDist = std::max(obj.audioSource.maxDistance, minDist + 0.5f); ma_sound_set_looping(&snd.sound, obj.audioSource.loop ? MA_TRUE : MA_FALSE); ma_sound_set_volume(&snd.sound, obj.audioSource.volume); ma_sound_set_spatialization_enabled(&snd.sound, obj.audioSource.spatial ? MA_TRUE : MA_FALSE); - ma_sound_set_min_distance(&snd.sound, obj.audioSource.minDistance); - ma_sound_set_max_distance(&snd.sound, obj.audioSource.maxDistance); + ma_sound_set_min_distance(&snd.sound, minDist); + ma_sound_set_max_distance(&snd.sound, maxDist); ma_sound_set_position(&snd.sound, obj.position.x, obj.position.y, obj.position.z); if (!ma_sound_is_playing(&snd.sound) && !snd.started && obj.audioSource.playOnStart && obj.audioSource.enabled) { @@ -120,20 +134,24 @@ void AudioSystem::update(const std::vector& objects, const Camera& auto eraseIt = activeSounds.find(obj.id); if (!obj.enabled || !obj.audioSource.enabled || obj.audioSource.clipPath.empty()) { if (eraseIt != activeSounds.end()) { - ma_sound_uninit(&eraseIt->second.sound); + if (eraseIt->second) { + ma_sound_uninit(&eraseIt->second->sound); + } activeSounds.erase(eraseIt); } continue; } if (ensureSoundFor(obj)) { - refreshSoundParams(obj, activeSounds[obj.id]); + refreshSoundParams(obj, *activeSounds[obj.id]); } } for (auto it = activeSounds.begin(); it != activeSounds.end(); ) { if (stillPresent.find(it->first) == stillPresent.end()) { - ma_sound_uninit(&it->second.sound); + if (it->second) { + ma_sound_uninit(&it->second->sound); + } it = activeSounds.erase(it); } else { ++it; @@ -202,7 +220,7 @@ bool AudioSystem::seekPreview(const std::string& path, double seconds) { bool AudioSystem::playObjectSound(const SceneObject& obj) { if (!obj.hasAudioSource || obj.audioSource.clipPath.empty() || !obj.audioSource.enabled) return false; if (!ensureSoundFor(obj)) return false; - ActiveSound& snd = activeSounds[obj.id]; + ActiveSound& snd = *activeSounds[obj.id]; snd.started = true; return ma_sound_start(&snd.sound) == MA_SUCCESS; } @@ -210,19 +228,20 @@ bool AudioSystem::playObjectSound(const SceneObject& obj) { bool AudioSystem::stopObjectSound(int objectId) { auto it = activeSounds.find(objectId); if (it == activeSounds.end()) return false; - return ma_sound_stop(&it->second.sound) == MA_SUCCESS; + if (!it->second) return false; + return ma_sound_stop(&it->second->sound) == MA_SUCCESS; } bool AudioSystem::setObjectLoop(const SceneObject& obj, bool loop) { if (!ensureSoundFor(obj)) return false; - ActiveSound& snd = activeSounds[obj.id]; + ActiveSound& snd = *activeSounds[obj.id]; ma_sound_set_looping(&snd.sound, loop ? MA_TRUE : MA_FALSE); return true; } bool AudioSystem::setObjectVolume(const SceneObject& obj, float volume) { if (!ensureSoundFor(obj)) return false; - ActiveSound& snd = activeSounds[obj.id]; + ActiveSound& snd = *activeSounds[obj.id]; ma_sound_set_volume(&snd.sound, volume); return true; } diff --git a/src/AudioSystem.h b/src/AudioSystem.h index 07bd2cc..d747642 100644 --- a/src/AudioSystem.h +++ b/src/AudioSystem.h @@ -49,8 +49,9 @@ private: ma_engine engine{}; bool initialized = false; - std::unordered_map activeSounds; + std::unordered_map> activeSounds; std::unordered_map previewCache; + std::unordered_set missingClips; ma_sound previewSound{}; bool previewActive = false; diff --git a/src/EditorWindows/SceneWindows.cpp b/src/EditorWindows/SceneWindows.cpp index f90f709..81b8672 100644 --- a/src/EditorWindows/SceneWindows.cpp +++ b/src/EditorWindows/SceneWindows.cpp @@ -35,9 +35,22 @@ void Engine::renderHierarchyPanel() { ImGui::PushStyleColor(ImGuiCol_ChildBg, headerBg); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8.0f, 4.0f)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 4.0f)); - ImGui::BeginChild("HierarchyHeader", ImVec2(0, 50), true, ImGuiWindowFlags_NoScrollbar); + ImGui::BeginChild("HierarchyHeader", ImVec2(0, 74), true, ImGuiWindowFlags_NoScrollbar); ImGui::SetNextItemWidth(-1); ImGui::InputTextWithHint("##Search", "Search...", searchBuffer, sizeof(searchBuffer)); + ImGui::Spacing(); + ImGui::Checkbox("Texture Preview", &hierarchyShowTexturePreview); + ImGui::SameLine(); + ImGui::BeginDisabled(!hierarchyShowTexturePreview); + ImGui::TextDisabled("Filter"); + ImGui::SameLine(); + const char* filterOptions[] = { "Bilinear", "Nearest" }; + int filterIndex = hierarchyPreviewNearest ? 1 : 0; + ImGui::SetNextItemWidth(120.0f); + if (ImGui::Combo("##HierarchyTexFilter", &filterIndex, filterOptions, IM_ARRAYSIZE(filterOptions))) { + hierarchyPreviewNearest = (filterIndex == 1); + } + ImGui::EndDisabled(); ImGui::EndChild(); ImGui::PopStyleVar(2); ImGui::PopStyleColor(); @@ -181,6 +194,38 @@ void Engine::renderObjectNode(SceneObject& obj, const std::string& filter) { ImGui::EndPopup(); } + if (hierarchyShowTexturePreview) { + const std::string* previewPath = nullptr; + if (!obj.albedoTexturePath.empty()) { + previewPath = &obj.albedoTexturePath; + } else if (obj.useOverlay && !obj.overlayTexturePath.empty()) { + previewPath = &obj.overlayTexturePath; + } + + if (previewPath) { + auto overrideIt = texturePreviewFilterOverrides.find(*previewPath); + bool previewNearest = (overrideIt != texturePreviewFilterOverrides.end()) + ? overrideIt->second + : hierarchyPreviewNearest; + Texture* previewTex = renderer.getTexturePreview(*previewPath, previewNearest); + if (previewTex && previewTex->GetID()) { + ImGuiStyle& style = ImGui::GetStyle(); + ImVec2 itemMin = ImGui::GetItemRectMin(); + ImVec2 itemMax = ImGui::GetItemRectMax(); + float lineHeight = itemMax.y - itemMin.y; + float previewSize = std::max(12.0f, lineHeight - 4.0f); + float rightEdge = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x; + float previewX = rightEdge - previewSize - style.WindowPadding.x; + float previewY = itemMin.y + (lineHeight - previewSize) * 0.5f; + ImVec2 pMin(previewX, previewY); + ImVec2 pMax(previewX + previewSize, previewY + previewSize); + ImGui::GetWindowDrawList()->AddImage( + (ImTextureID)(intptr_t)previewTex->GetID(), + pMin, pMax, ImVec2(0, 1), ImVec2(1, 0)); + } + } + } + if (nodeOpen) { for (int childId : obj.childIds) { auto it = std::find_if(sceneObjects.begin(), sceneObjects.end(), @@ -201,6 +246,8 @@ void Engine::renderInspectorPanel() { fs::path selectedAudioPath; bool browserHasAudio = false; const AudioClipPreview* selectedAudioPreview = nullptr; + fs::path selectedTexturePath; + bool browserHasTexture = false; if (!fileBrowser.selectedFile.empty() && fs::exists(fileBrowser.selectedFile)) { fs::directory_entry entry(fileBrowser.selectedFile); FileCategory cat = fileBrowser.getFileCategory(entry); @@ -229,6 +276,10 @@ void Engine::renderInspectorPanel() { browserHasAudio = true; selectedAudioPreview = audio.getPreview(selectedAudioPath.string()); } + if (cat == FileCategory::Texture) { + selectedTexturePath = entry.path(); + browserHasTexture = true; + } } else { inspectedMaterialPath.clear(); inspectedMaterialValid = false; @@ -555,11 +606,68 @@ void Engine::renderInspectorPanel() { ImGui::PopStyleColor(); }; + auto renderTextureAssetPanel = [&](const char* headerTitle) { + if (!browserHasTexture) return; + + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.35f, 0.35f, 0.55f, 1.0f)); + if (ImGui::CollapsingHeader(headerTitle, ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Indent(8.0f); + + ImGui::TextDisabled("%s", selectedTexturePath.filename().string().c_str()); + ImGui::TextColored(ImVec4(0.8f, 0.65f, 0.95f, 1.0f), "%s", selectedTexturePath.string().c_str()); + + bool hasOverride = texturePreviewFilterOverrides.find(selectedTexturePath.string()) != texturePreviewFilterOverrides.end(); + bool previewNearest = hasOverride ? texturePreviewFilterOverrides[selectedTexturePath.string()] : hierarchyPreviewNearest; + Texture* previewTex = renderer.getTexturePreview(selectedTexturePath.string(), previewNearest); + + ImGui::Spacing(); + if (previewTex && previewTex->GetID()) { + float maxWidth = ImGui::GetContentRegionAvail().x; + float size = std::min(maxWidth, 160.0f); + float aspect = previewTex->GetHeight() > 0 ? (previewTex->GetWidth() / static_cast(previewTex->GetHeight())) : 1.0f; + ImVec2 imageSize(size, size); + if (aspect > 1.0f) { + imageSize.y = size / aspect; + } else if (aspect > 0.0f) { + imageSize.x = size * aspect; + } + ImGui::Image((ImTextureID)(intptr_t)previewTex->GetID(), imageSize, ImVec2(0, 1), ImVec2(1, 0)); + ImGui::Text("Size: %d x %d", previewTex->GetWidth(), previewTex->GetHeight()); + } else { + ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.6f, 1.0f), "Unable to load texture preview."); + } + + ImGui::Spacing(); + if (ImGui::Checkbox("Override Hierarchy Filter", &hasOverride)) { + if (hasOverride) { + texturePreviewFilterOverrides[selectedTexturePath.string()] = hierarchyPreviewNearest; + } else { + texturePreviewFilterOverrides.erase(selectedTexturePath.string()); + } + } + ImGui::BeginDisabled(!hasOverride); + const char* filterOptions[] = { "Bilinear", "Nearest" }; + int filterIndex = previewNearest ? 1 : 0; + if (ImGui::Combo("Preview Filter", &filterIndex, filterOptions, IM_ARRAYSIZE(filterOptions))) { + texturePreviewFilterOverrides[selectedTexturePath.string()] = (filterIndex == 1); + } + ImGui::EndDisabled(); + if (!hasOverride) { + ImGui::TextDisabled("Using global: %s", hierarchyPreviewNearest ? "Nearest" : "Bilinear"); + } + + ImGui::Unindent(8.0f); + } + ImGui::PopStyleColor(); + }; + if (selectedObjectIds.empty()) { if (browserHasMaterial) { renderMaterialAssetPanel("Material Asset", true); } else if (browserHasAudio) { renderAudioAssetPanel("Audio Clip", nullptr); + } else if (browserHasTexture) { + renderTextureAssetPanel("Texture"); } else { ImGui::TextDisabled("No object selected"); } diff --git a/src/Engine.cpp b/src/Engine.cpp index 99960b8..cd4d5fa 100644 --- a/src/Engine.cpp +++ b/src/Engine.cpp @@ -1281,10 +1281,34 @@ bool Engine::getRigidbodyVelocityFromScript(int id, glm::vec3& outVelocity) { return physics.getLinearVelocity(id, outVelocity); } +bool Engine::setRigidbodyAngularVelocityFromScript(int id, const glm::vec3& velocity) { + return physics.setAngularVelocity(id, velocity); +} + +bool Engine::getRigidbodyAngularVelocityFromScript(int id, glm::vec3& outVelocity) { + return physics.getAngularVelocity(id, outVelocity); +} + bool Engine::teleportPhysicsActorFromScript(int id, const glm::vec3& position, const glm::vec3& rotationDeg) { return physics.setActorPose(id, position, rotationDeg); } +bool Engine::addRigidbodyForceFromScript(int id, const glm::vec3& force) { + return physics.addForce(id, force); +} + +bool Engine::addRigidbodyImpulseFromScript(int id, const glm::vec3& impulse) { + return physics.addImpulse(id, impulse); +} + +bool Engine::addRigidbodyTorqueFromScript(int id, const glm::vec3& torque) { + return physics.addTorque(id, torque); +} + +bool Engine::addRigidbodyAngularImpulseFromScript(int id, const glm::vec3& impulse) { + return physics.addAngularImpulse(id, impulse); +} + bool Engine::playAudioFromScript(int id) { SceneObject* obj = findObjectById(id); if (!obj || !obj->hasAudioSource) return false; diff --git a/src/Engine.h b/src/Engine.h index a5e5538..f211ac8 100644 --- a/src/Engine.h +++ b/src/Engine.h @@ -13,6 +13,7 @@ #include "AudioSystem.h" #include "PackageManager.h" #include "../include/Window/Window.h" +#include void window_size_callback(GLFWwindow* window, int width, int height); @@ -98,6 +99,9 @@ private: float fileBrowserIconScale = 1.0f; // 0.5 to 2.0 range bool showEnvironmentWindow = true; bool showCameraWindow = true; + bool hierarchyShowTexturePreview = false; + bool hierarchyPreviewNearest = false; + std::unordered_map texturePreviewFilterOverrides; bool isPlaying = false; bool isPaused = false; bool showViewOutput = true; @@ -235,7 +239,13 @@ public: // Script-accessible physics helpers bool setRigidbodyVelocityFromScript(int id, const glm::vec3& velocity); bool getRigidbodyVelocityFromScript(int id, glm::vec3& outVelocity); + bool setRigidbodyAngularVelocityFromScript(int id, const glm::vec3& velocity); + bool getRigidbodyAngularVelocityFromScript(int id, glm::vec3& outVelocity); bool teleportPhysicsActorFromScript(int id, const glm::vec3& position, const glm::vec3& rotationDeg); + bool addRigidbodyForceFromScript(int id, const glm::vec3& force); + bool addRigidbodyImpulseFromScript(int id, const glm::vec3& impulse); + bool addRigidbodyTorqueFromScript(int id, const glm::vec3& torque); + bool addRigidbodyAngularImpulseFromScript(int id, const glm::vec3& impulse); // Audio control exposed to scripts bool playAudioFromScript(int id); bool stopAudioFromScript(int id); diff --git a/src/PhysicsSystem.cpp b/src/PhysicsSystem.cpp index 4a5a6e1..8ea2fcd 100644 --- a/src/PhysicsSystem.cpp +++ b/src/PhysicsSystem.cpp @@ -398,6 +398,20 @@ bool PhysicsSystem::setLinearVelocity(int id, const glm::vec3& velocity) { return false; } +bool PhysicsSystem::setAngularVelocity(int id, const glm::vec3& velocity) { +#ifdef MODULARITY_ENABLE_PHYSX + auto it = mActors.find(id); + if (it == mActors.end()) return false; + ActorRecord& rec = it->second; + if (!rec.actor || !rec.isDynamic || rec.isKinematic) return false; + if (PxRigidDynamic* dyn = rec.actor->is()) { + dyn->setAngularVelocity(ToPxVec3(velocity)); + return true; + } +#endif + return false; +} + bool PhysicsSystem::setActorYaw(int id, float yawDegrees) { #ifdef MODULARITY_ENABLE_PHYSX auto it = mActors.find(id); @@ -428,6 +442,21 @@ bool PhysicsSystem::getLinearVelocity(int id, glm::vec3& outVelocity) const { return false; } +bool PhysicsSystem::getAngularVelocity(int id, glm::vec3& outVelocity) const { +#ifdef MODULARITY_ENABLE_PHYSX + auto it = mActors.find(id); + if (it == mActors.end()) return false; + const ActorRecord& rec = it->second; + if (!rec.actor || !rec.isDynamic || rec.isKinematic) return false; + if (const PxRigidDynamic* dyn = rec.actor->is()) { + PxVec3 v = dyn->getAngularVelocity(); + outVelocity = glm::vec3(v.x, v.y, v.z); + return true; + } +#endif + return false; +} + bool PhysicsSystem::setActorPose(int id, const glm::vec3& position, const glm::vec3& rotationDeg) { #ifdef MODULARITY_ENABLE_PHYSX auto it = mActors.find(id); @@ -443,6 +472,62 @@ bool PhysicsSystem::setActorPose(int id, const glm::vec3& position, const glm::v #endif } +bool PhysicsSystem::addForce(int id, const glm::vec3& force) { +#ifdef MODULARITY_ENABLE_PHYSX + auto it = mActors.find(id); + if (it == mActors.end()) return false; + ActorRecord& rec = it->second; + if (!rec.actor || !rec.isDynamic || rec.isKinematic) return false; + if (PxRigidDynamic* dyn = rec.actor->is()) { + dyn->addForce(ToPxVec3(force), PxForceMode::eFORCE); + return true; + } +#endif + return false; +} + +bool PhysicsSystem::addImpulse(int id, const glm::vec3& impulse) { +#ifdef MODULARITY_ENABLE_PHYSX + auto it = mActors.find(id); + if (it == mActors.end()) return false; + ActorRecord& rec = it->second; + if (!rec.actor || !rec.isDynamic || rec.isKinematic) return false; + if (PxRigidDynamic* dyn = rec.actor->is()) { + dyn->addForce(ToPxVec3(impulse), PxForceMode::eIMPULSE); + return true; + } +#endif + return false; +} + +bool PhysicsSystem::addTorque(int id, const glm::vec3& torque) { +#ifdef MODULARITY_ENABLE_PHYSX + auto it = mActors.find(id); + if (it == mActors.end()) return false; + ActorRecord& rec = it->second; + if (!rec.actor || !rec.isDynamic || rec.isKinematic) return false; + if (PxRigidDynamic* dyn = rec.actor->is()) { + dyn->addTorque(ToPxVec3(torque), PxForceMode::eFORCE); + return true; + } +#endif + return false; +} + +bool PhysicsSystem::addAngularImpulse(int id, const glm::vec3& impulse) { +#ifdef MODULARITY_ENABLE_PHYSX + auto it = mActors.find(id); + if (it == mActors.end()) return false; + ActorRecord& rec = it->second; + if (!rec.actor || !rec.isDynamic || rec.isKinematic) return false; + if (PxRigidDynamic* dyn = rec.actor->is()) { + dyn->addTorque(ToPxVec3(impulse), PxForceMode::eIMPULSE); + return true; + } +#endif + return false; +} + bool PhysicsSystem::raycastClosest(const glm::vec3& origin, const glm::vec3& dir, float distance, int ignoreId, glm::vec3* hitPos, glm::vec3* hitNormal, float* hitDistance) const { #ifdef MODULARITY_ENABLE_PHYSX @@ -544,8 +629,15 @@ bool PhysicsSystem::init() { return false; } void PhysicsSystem::shutdown() {} bool PhysicsSystem::isReady() const { return false; } bool PhysicsSystem::setLinearVelocity(int, const glm::vec3&) { return false; } +bool PhysicsSystem::setAngularVelocity(int, const glm::vec3&) { return false; } bool PhysicsSystem::setActorYaw(int, float) { return false; } bool PhysicsSystem::getLinearVelocity(int, glm::vec3&) const { return false; } +bool PhysicsSystem::getAngularVelocity(int, glm::vec3&) const { return false; } +bool PhysicsSystem::setActorPose(int, const glm::vec3&, const glm::vec3&) { return false; } +bool PhysicsSystem::addForce(int, const glm::vec3&) { return false; } +bool PhysicsSystem::addImpulse(int, const glm::vec3&) { return false; } +bool PhysicsSystem::addTorque(int, const glm::vec3&) { return false; } +bool PhysicsSystem::addAngularImpulse(int, const glm::vec3&) { return false; } void PhysicsSystem::onPlayStart(const std::vector&) {} void PhysicsSystem::onPlayStop() {} void PhysicsSystem::simulate(float, std::vector&) {} diff --git a/src/PhysicsSystem.h b/src/PhysicsSystem.h index 1cb3ecd..b1d74da 100644 --- a/src/PhysicsSystem.h +++ b/src/PhysicsSystem.h @@ -16,9 +16,15 @@ public: void shutdown(); bool isReady() const; bool setLinearVelocity(int id, const glm::vec3& velocity); + bool setAngularVelocity(int id, const glm::vec3& velocity); bool setActorYaw(int id, float yawDegrees); bool getLinearVelocity(int id, glm::vec3& outVelocity) const; + bool getAngularVelocity(int id, glm::vec3& outVelocity) const; bool setActorPose(int id, const glm::vec3& position, const glm::vec3& rotationDeg); + bool addForce(int id, const glm::vec3& force); + bool addImpulse(int id, const glm::vec3& impulse); + bool addTorque(int id, const glm::vec3& torque); + bool addAngularImpulse(int id, const glm::vec3& impulse); bool raycastClosest(const glm::vec3& origin, const glm::vec3& dir, float distance, int ignoreId, glm::vec3* hitPos = nullptr, glm::vec3* hitNormal = nullptr, float* hitDistance = nullptr) const; diff --git a/src/Rendering.cpp b/src/Rendering.cpp index b27da00..5430163 100644 --- a/src/Rendering.cpp +++ b/src/Rendering.cpp @@ -480,6 +480,23 @@ Texture* Renderer::getTexture(const std::string& path) { return raw; } +Texture* Renderer::getTexturePreview(const std::string& path, bool nearest) { + if (path.empty()) return nullptr; + auto& cache = nearest ? previewTextureCacheNearest : previewTextureCacheLinear; + auto it = cache.find(path); + if (it != cache.end()) return it->second.get(); + + GLenum minFilter = nearest ? GL_NEAREST : GL_LINEAR_MIPMAP_LINEAR; + GLenum magFilter = nearest ? GL_NEAREST : GL_LINEAR; + auto tex = std::make_unique(path, GL_REPEAT, GL_REPEAT, minFilter, magFilter); + if (!tex->GetID()) { + return nullptr; + } + Texture* raw = tex.get(); + cache[path] = std::move(tex); + return raw; +} + void Renderer::initialize() { shader = new Shader(defaultVertPath.c_str(), defaultFragPath.c_str()); defaultShader = shader; diff --git a/src/Rendering.h b/src/Rendering.h index 58678f8..aa06c6e 100644 --- a/src/Rendering.h +++ b/src/Rendering.h @@ -86,6 +86,8 @@ private: Texture* texture1 = nullptr; Texture* texture2 = nullptr; std::unordered_map> textureCache; + std::unordered_map> previewTextureCacheLinear; + std::unordered_map> previewTextureCacheNearest; struct ShaderEntry { std::unique_ptr shader; fs::file_time_type vertTime; @@ -131,6 +133,7 @@ public: void initialize(); Texture* getTexture(const std::string& path); + Texture* getTexturePreview(const std::string& path, bool nearest); Shader* getShader(const std::string& vert, const std::string& frag); bool forceReloadShader(const std::string& vert, const std::string& frag); void setAmbientColor(const glm::vec3& color) { ambientColor = color; } diff --git a/src/ScriptRuntime.cpp b/src/ScriptRuntime.cpp index d133878..9cfb32e 100644 --- a/src/ScriptRuntime.cpp +++ b/src/ScriptRuntime.cpp @@ -122,6 +122,43 @@ bool ScriptContext::GetRigidbodyVelocity(glm::vec3& outVelocity) const { return engine->getRigidbodyVelocityFromScript(object->id, outVelocity); } +bool ScriptContext::SetRigidbodyAngularVelocity(const glm::vec3& velocity) { + if (!engine || !object || !HasRigidbody()) return false; + return engine->setRigidbodyAngularVelocityFromScript(object->id, velocity); +} + +bool ScriptContext::GetRigidbodyAngularVelocity(glm::vec3& outVelocity) const { + if (!engine || !object || !HasRigidbody()) return false; + return engine->getRigidbodyAngularVelocityFromScript(object->id, outVelocity); +} + +bool ScriptContext::AddRigidbodyForce(const glm::vec3& force) { + if (!engine || !object || !HasRigidbody()) return false; + return engine->addRigidbodyForceFromScript(object->id, force); +} + +bool ScriptContext::AddRigidbodyImpulse(const glm::vec3& impulse) { + if (!engine || !object || !HasRigidbody()) return false; + return engine->addRigidbodyImpulseFromScript(object->id, impulse); +} + +bool ScriptContext::AddRigidbodyTorque(const glm::vec3& torque) { + if (!engine || !object || !HasRigidbody()) return false; + return engine->addRigidbodyTorqueFromScript(object->id, torque); +} + +bool ScriptContext::AddRigidbodyAngularImpulse(const glm::vec3& impulse) { + if (!engine || !object || !HasRigidbody()) return false; + return engine->addRigidbodyAngularImpulseFromScript(object->id, impulse); +} + +bool ScriptContext::SetRigidbodyRotation(const glm::vec3& rotDeg) { + if (!engine || !object || !HasRigidbody()) return false; + object->rotation = NormalizeEulerDegrees(rotDeg); + MarkDirty(); + return engine->teleportPhysicsActorFromScript(object->id, object->position, object->rotation); +} + bool ScriptContext::TeleportRigidbody(const glm::vec3& pos, const glm::vec3& rotDeg) { if (!engine || !object) return false; object->position = pos; diff --git a/src/ScriptRuntime.h b/src/ScriptRuntime.h index b0a9ba2..3e24a71 100644 --- a/src/ScriptRuntime.h +++ b/src/ScriptRuntime.h @@ -39,6 +39,13 @@ struct ScriptContext { bool HasRigidbody() const; bool SetRigidbodyVelocity(const glm::vec3& velocity); bool GetRigidbodyVelocity(glm::vec3& outVelocity) const; + bool SetRigidbodyAngularVelocity(const glm::vec3& velocity); + bool GetRigidbodyAngularVelocity(glm::vec3& outVelocity) const; + bool AddRigidbodyForce(const glm::vec3& force); + bool AddRigidbodyImpulse(const glm::vec3& impulse); + bool AddRigidbodyTorque(const glm::vec3& torque); + bool AddRigidbodyAngularImpulse(const glm::vec3& impulse); + bool SetRigidbodyRotation(const glm::vec3& rotDeg); bool TeleportRigidbody(const glm::vec3& pos, const glm::vec3& rotDeg); // Audio helpers bool HasAudioSource() const;