Well, unfinished texture filtering system + Added Better Audio Source Loading and fixed up some stuff within the rendering.

This commit is contained in:
Anemunt
2025-12-22 19:48:44 -05:00
parent ff3bf6ee21
commit 534d513be2
13 changed files with 366 additions and 24 deletions

View File

@@ -40,7 +40,7 @@ std::string getSetting(const ScriptContext& ctx, const std::string& key, const s
void loadSettings(ScriptContext& ctx) { void loadSettings(ScriptContext& ctx) {
if (!ctx.script || !ctx.object) return; 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; settingsLoadedForId = ctx.object->id;
settingsLoadedForScript = ctx.script; settingsLoadedForScript = ctx.script;

View File

@@ -23,6 +23,12 @@
Available methods: Available methods:
- `FindObjectByName`, `FindObjectById` - `FindObjectByName`, `FindObjectById`
- `SetPosition`, `SetRotation`, `SetScale` - `SetPosition`, `SetRotation`, `SetScale`
- `HasRigidbody`
- `SetRigidbodyVelocity`, `GetRigidbodyVelocity`
- `SetRigidbodyAngularVelocity`, `GetRigidbodyAngularVelocity`
- `AddRigidbodyForce`, `AddRigidbodyImpulse`
- `AddRigidbodyTorque`, `AddRigidbodyAngularImpulse`
- `SetRigidbodyRotation`, `TeleportRigidbody`
- `MarkDirty` (flags the project as having unsaved changes) - `MarkDirty` (flags the project as having unsaved changes)
Fields: Fields:
- `engine`: pointer to the Engine - `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. - Scripts tick for all objects every frame, even if not selected.
- Spec/Test toggles are global (main menu → Scripts). - 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. - 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
```

View File

@@ -30,7 +30,9 @@ void AudioSystem::shutdown() {
void AudioSystem::destroyActiveSounds() { void AudioSystem::destroyActiveSounds() {
for (auto& kv : activeSounds) { for (auto& kv : activeSounds) {
ma_sound_uninit(&kv.second.sound); if (kv.second) {
ma_sound_uninit(&kv.second->sound);
}
} }
activeSounds.clear(); activeSounds.clear();
} }
@@ -42,7 +44,7 @@ void AudioSystem::onPlayStart(const std::vector<SceneObject>& objects) {
if (!obj.enabled || !obj.hasAudioSource || obj.audioSource.clipPath.empty()) continue; if (!obj.enabled || !obj.hasAudioSource || obj.audioSource.clipPath.empty()) continue;
if (!obj.audioSource.enabled) continue; if (!obj.audioSource.enabled) continue;
if (ensureSoundFor(obj) && obj.audioSource.playOnStart) { 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) { bool AudioSystem::ensureSoundFor(const SceneObject& obj) {
auto it = activeSounds.find(obj.id); auto it = activeSounds.find(obj.id);
if (it != activeSounds.end()) { if (it != activeSounds.end()) {
if (it->second.clipPath == obj.audioSource.clipPath) { if (it->second && it->second->clipPath == obj.audioSource.clipPath) {
refreshSoundParams(obj, it->second); refreshSoundParams(obj, *it->second);
return true; return true;
} }
ma_sound_uninit(&it->second.sound); if (it->second) {
ma_sound_uninit(&it->second->sound);
}
activeSounds.erase(it); 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; if (!initialized && !init()) return false;
ActiveSound snd{}; auto snd = std::make_unique<ActiveSound>();
ma_result res = ma_sound_init_from_file( ma_result res = ma_sound_init_from_file(
&engine, &engine,
obj.audioSource.clipPath.c_str(), obj.audioSource.clipPath.c_str(),
MA_SOUND_FLAG_STREAM, MA_SOUND_FLAG_STREAM,
nullptr, nullptr,
nullptr, nullptr,
&snd.sound &snd->sound
); );
if (res != MA_SUCCESS) { if (res != MA_SUCCESS) {
std::cerr << "AudioSystem: failed to load " << obj.audioSource.clipPath << " (" << res << ")\n"; std::cerr << "AudioSystem: failed to load " << obj.audioSource.clipPath << " (" << res << ")\n";
return false; return false;
} }
snd.clipPath = obj.audioSource.clipPath; snd->clipPath = obj.audioSource.clipPath;
snd.spatial = obj.audioSource.spatial; snd->spatial = obj.audioSource.spatial;
snd.started = false; snd->started = false;
refreshSoundParams(obj, snd); refreshSoundParams(obj, *snd);
activeSounds[obj.id] = std::move(snd); activeSounds.emplace(obj.id, std::move(snd));
return true; return true;
} }
void AudioSystem::refreshSoundParams(const SceneObject& obj, ActiveSound& snd) { 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_looping(&snd.sound, obj.audioSource.loop ? MA_TRUE : MA_FALSE);
ma_sound_set_volume(&snd.sound, obj.audioSource.volume); 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_spatialization_enabled(&snd.sound, obj.audioSource.spatial ? MA_TRUE : MA_FALSE);
ma_sound_set_min_distance(&snd.sound, obj.audioSource.minDistance); ma_sound_set_min_distance(&snd.sound, minDist);
ma_sound_set_max_distance(&snd.sound, obj.audioSource.maxDistance); ma_sound_set_max_distance(&snd.sound, maxDist);
ma_sound_set_position(&snd.sound, obj.position.x, obj.position.y, obj.position.z); 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) { 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<SceneObject>& objects, const Camera&
auto eraseIt = activeSounds.find(obj.id); auto eraseIt = activeSounds.find(obj.id);
if (!obj.enabled || !obj.audioSource.enabled || obj.audioSource.clipPath.empty()) { if (!obj.enabled || !obj.audioSource.enabled || obj.audioSource.clipPath.empty()) {
if (eraseIt != activeSounds.end()) { if (eraseIt != activeSounds.end()) {
ma_sound_uninit(&eraseIt->second.sound); if (eraseIt->second) {
ma_sound_uninit(&eraseIt->second->sound);
}
activeSounds.erase(eraseIt); activeSounds.erase(eraseIt);
} }
continue; continue;
} }
if (ensureSoundFor(obj)) { if (ensureSoundFor(obj)) {
refreshSoundParams(obj, activeSounds[obj.id]); refreshSoundParams(obj, *activeSounds[obj.id]);
} }
} }
for (auto it = activeSounds.begin(); it != activeSounds.end(); ) { for (auto it = activeSounds.begin(); it != activeSounds.end(); ) {
if (stillPresent.find(it->first) == stillPresent.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); it = activeSounds.erase(it);
} else { } else {
++it; ++it;
@@ -202,7 +220,7 @@ bool AudioSystem::seekPreview(const std::string& path, double seconds) {
bool AudioSystem::playObjectSound(const SceneObject& obj) { bool AudioSystem::playObjectSound(const SceneObject& obj) {
if (!obj.hasAudioSource || obj.audioSource.clipPath.empty() || !obj.audioSource.enabled) return false; if (!obj.hasAudioSource || obj.audioSource.clipPath.empty() || !obj.audioSource.enabled) return false;
if (!ensureSoundFor(obj)) return false; if (!ensureSoundFor(obj)) return false;
ActiveSound& snd = activeSounds[obj.id]; ActiveSound& snd = *activeSounds[obj.id];
snd.started = true; snd.started = true;
return ma_sound_start(&snd.sound) == MA_SUCCESS; return ma_sound_start(&snd.sound) == MA_SUCCESS;
} }
@@ -210,19 +228,20 @@ bool AudioSystem::playObjectSound(const SceneObject& obj) {
bool AudioSystem::stopObjectSound(int objectId) { bool AudioSystem::stopObjectSound(int objectId) {
auto it = activeSounds.find(objectId); auto it = activeSounds.find(objectId);
if (it == activeSounds.end()) return false; 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) { bool AudioSystem::setObjectLoop(const SceneObject& obj, bool loop) {
if (!ensureSoundFor(obj)) return false; 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); ma_sound_set_looping(&snd.sound, loop ? MA_TRUE : MA_FALSE);
return true; return true;
} }
bool AudioSystem::setObjectVolume(const SceneObject& obj, float volume) { bool AudioSystem::setObjectVolume(const SceneObject& obj, float volume) {
if (!ensureSoundFor(obj)) return false; if (!ensureSoundFor(obj)) return false;
ActiveSound& snd = activeSounds[obj.id]; ActiveSound& snd = *activeSounds[obj.id];
ma_sound_set_volume(&snd.sound, volume); ma_sound_set_volume(&snd.sound, volume);
return true; return true;
} }

View File

@@ -49,8 +49,9 @@ private:
ma_engine engine{}; ma_engine engine{};
bool initialized = false; bool initialized = false;
std::unordered_map<int, ActiveSound> activeSounds; std::unordered_map<int, std::unique_ptr<ActiveSound>> activeSounds;
std::unordered_map<std::string, AudioClipPreview> previewCache; std::unordered_map<std::string, AudioClipPreview> previewCache;
std::unordered_set<std::string> missingClips;
ma_sound previewSound{}; ma_sound previewSound{};
bool previewActive = false; bool previewActive = false;

View File

@@ -35,9 +35,22 @@ void Engine::renderHierarchyPanel() {
ImGui::PushStyleColor(ImGuiCol_ChildBg, headerBg); ImGui::PushStyleColor(ImGuiCol_ChildBg, headerBg);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8.0f, 4.0f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(8.0f, 4.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.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::SetNextItemWidth(-1);
ImGui::InputTextWithHint("##Search", "Search...", searchBuffer, sizeof(searchBuffer)); 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::EndChild();
ImGui::PopStyleVar(2); ImGui::PopStyleVar(2);
ImGui::PopStyleColor(); ImGui::PopStyleColor();
@@ -181,6 +194,38 @@ void Engine::renderObjectNode(SceneObject& obj, const std::string& filter) {
ImGui::EndPopup(); 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) { if (nodeOpen) {
for (int childId : obj.childIds) { for (int childId : obj.childIds) {
auto it = std::find_if(sceneObjects.begin(), sceneObjects.end(), auto it = std::find_if(sceneObjects.begin(), sceneObjects.end(),
@@ -201,6 +246,8 @@ void Engine::renderInspectorPanel() {
fs::path selectedAudioPath; fs::path selectedAudioPath;
bool browserHasAudio = false; bool browserHasAudio = false;
const AudioClipPreview* selectedAudioPreview = nullptr; const AudioClipPreview* selectedAudioPreview = nullptr;
fs::path selectedTexturePath;
bool browserHasTexture = false;
if (!fileBrowser.selectedFile.empty() && fs::exists(fileBrowser.selectedFile)) { if (!fileBrowser.selectedFile.empty() && fs::exists(fileBrowser.selectedFile)) {
fs::directory_entry entry(fileBrowser.selectedFile); fs::directory_entry entry(fileBrowser.selectedFile);
FileCategory cat = fileBrowser.getFileCategory(entry); FileCategory cat = fileBrowser.getFileCategory(entry);
@@ -229,6 +276,10 @@ void Engine::renderInspectorPanel() {
browserHasAudio = true; browserHasAudio = true;
selectedAudioPreview = audio.getPreview(selectedAudioPath.string()); selectedAudioPreview = audio.getPreview(selectedAudioPath.string());
} }
if (cat == FileCategory::Texture) {
selectedTexturePath = entry.path();
browserHasTexture = true;
}
} else { } else {
inspectedMaterialPath.clear(); inspectedMaterialPath.clear();
inspectedMaterialValid = false; inspectedMaterialValid = false;
@@ -555,11 +606,68 @@ void Engine::renderInspectorPanel() {
ImGui::PopStyleColor(); 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<float>(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 (selectedObjectIds.empty()) {
if (browserHasMaterial) { if (browserHasMaterial) {
renderMaterialAssetPanel("Material Asset", true); renderMaterialAssetPanel("Material Asset", true);
} else if (browserHasAudio) { } else if (browserHasAudio) {
renderAudioAssetPanel("Audio Clip", nullptr); renderAudioAssetPanel("Audio Clip", nullptr);
} else if (browserHasTexture) {
renderTextureAssetPanel("Texture");
} else { } else {
ImGui::TextDisabled("No object selected"); ImGui::TextDisabled("No object selected");
} }

View File

@@ -1281,10 +1281,34 @@ bool Engine::getRigidbodyVelocityFromScript(int id, glm::vec3& outVelocity) {
return physics.getLinearVelocity(id, 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) { bool Engine::teleportPhysicsActorFromScript(int id, const glm::vec3& position, const glm::vec3& rotationDeg) {
return physics.setActorPose(id, position, 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) { bool Engine::playAudioFromScript(int id) {
SceneObject* obj = findObjectById(id); SceneObject* obj = findObjectById(id);
if (!obj || !obj->hasAudioSource) return false; if (!obj || !obj->hasAudioSource) return false;

View File

@@ -13,6 +13,7 @@
#include "AudioSystem.h" #include "AudioSystem.h"
#include "PackageManager.h" #include "PackageManager.h"
#include "../include/Window/Window.h" #include "../include/Window/Window.h"
#include <unordered_map>
void window_size_callback(GLFWwindow* window, int width, int height); 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 float fileBrowserIconScale = 1.0f; // 0.5 to 2.0 range
bool showEnvironmentWindow = true; bool showEnvironmentWindow = true;
bool showCameraWindow = true; bool showCameraWindow = true;
bool hierarchyShowTexturePreview = false;
bool hierarchyPreviewNearest = false;
std::unordered_map<std::string, bool> texturePreviewFilterOverrides;
bool isPlaying = false; bool isPlaying = false;
bool isPaused = false; bool isPaused = false;
bool showViewOutput = true; bool showViewOutput = true;
@@ -235,7 +239,13 @@ public:
// Script-accessible physics helpers // Script-accessible physics helpers
bool setRigidbodyVelocityFromScript(int id, const glm::vec3& velocity); bool setRigidbodyVelocityFromScript(int id, const glm::vec3& velocity);
bool getRigidbodyVelocityFromScript(int id, glm::vec3& outVelocity); 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 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 // Audio control exposed to scripts
bool playAudioFromScript(int id); bool playAudioFromScript(int id);
bool stopAudioFromScript(int id); bool stopAudioFromScript(int id);

View File

@@ -398,6 +398,20 @@ bool PhysicsSystem::setLinearVelocity(int id, const glm::vec3& velocity) {
return false; 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<PxRigidDynamic>()) {
dyn->setAngularVelocity(ToPxVec3(velocity));
return true;
}
#endif
return false;
}
bool PhysicsSystem::setActorYaw(int id, float yawDegrees) { bool PhysicsSystem::setActorYaw(int id, float yawDegrees) {
#ifdef MODULARITY_ENABLE_PHYSX #ifdef MODULARITY_ENABLE_PHYSX
auto it = mActors.find(id); auto it = mActors.find(id);
@@ -428,6 +442,21 @@ bool PhysicsSystem::getLinearVelocity(int id, glm::vec3& outVelocity) const {
return false; 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<PxRigidDynamic>()) {
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) { bool PhysicsSystem::setActorPose(int id, const glm::vec3& position, const glm::vec3& rotationDeg) {
#ifdef MODULARITY_ENABLE_PHYSX #ifdef MODULARITY_ENABLE_PHYSX
auto it = mActors.find(id); auto it = mActors.find(id);
@@ -443,6 +472,62 @@ bool PhysicsSystem::setActorPose(int id, const glm::vec3& position, const glm::v
#endif #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<PxRigidDynamic>()) {
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<PxRigidDynamic>()) {
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<PxRigidDynamic>()) {
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<PxRigidDynamic>()) {
dyn->addTorque(ToPxVec3(impulse), PxForceMode::eIMPULSE);
return true;
}
#endif
return false;
}
bool PhysicsSystem::raycastClosest(const glm::vec3& origin, const glm::vec3& dir, float distance, bool PhysicsSystem::raycastClosest(const glm::vec3& origin, const glm::vec3& dir, float distance,
int ignoreId, glm::vec3* hitPos, glm::vec3* hitNormal, float* hitDistance) const { int ignoreId, glm::vec3* hitPos, glm::vec3* hitNormal, float* hitDistance) const {
#ifdef MODULARITY_ENABLE_PHYSX #ifdef MODULARITY_ENABLE_PHYSX
@@ -544,8 +629,15 @@ bool PhysicsSystem::init() { return false; }
void PhysicsSystem::shutdown() {} void PhysicsSystem::shutdown() {}
bool PhysicsSystem::isReady() const { return false; } bool PhysicsSystem::isReady() const { return false; }
bool PhysicsSystem::setLinearVelocity(int, const glm::vec3&) { 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::setActorYaw(int, float) { return false; }
bool PhysicsSystem::getLinearVelocity(int, glm::vec3&) const { 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<SceneObject>&) {} void PhysicsSystem::onPlayStart(const std::vector<SceneObject>&) {}
void PhysicsSystem::onPlayStop() {} void PhysicsSystem::onPlayStop() {}
void PhysicsSystem::simulate(float, std::vector<SceneObject>&) {} void PhysicsSystem::simulate(float, std::vector<SceneObject>&) {}

View File

@@ -16,9 +16,15 @@ public:
void shutdown(); void shutdown();
bool isReady() const; bool isReady() const;
bool setLinearVelocity(int id, const glm::vec3& velocity); bool setLinearVelocity(int id, const glm::vec3& velocity);
bool setAngularVelocity(int id, const glm::vec3& velocity);
bool setActorYaw(int id, float yawDegrees); bool setActorYaw(int id, float yawDegrees);
bool getLinearVelocity(int id, glm::vec3& outVelocity) const; 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 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, bool raycastClosest(const glm::vec3& origin, const glm::vec3& dir, float distance,
int ignoreId, glm::vec3* hitPos = nullptr, int ignoreId, glm::vec3* hitPos = nullptr,
glm::vec3* hitNormal = nullptr, float* hitDistance = nullptr) const; glm::vec3* hitNormal = nullptr, float* hitDistance = nullptr) const;

View File

@@ -480,6 +480,23 @@ Texture* Renderer::getTexture(const std::string& path) {
return raw; 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<Texture>(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() { void Renderer::initialize() {
shader = new Shader(defaultVertPath.c_str(), defaultFragPath.c_str()); shader = new Shader(defaultVertPath.c_str(), defaultFragPath.c_str());
defaultShader = shader; defaultShader = shader;

View File

@@ -86,6 +86,8 @@ private:
Texture* texture1 = nullptr; Texture* texture1 = nullptr;
Texture* texture2 = nullptr; Texture* texture2 = nullptr;
std::unordered_map<std::string, std::unique_ptr<Texture>> textureCache; std::unordered_map<std::string, std::unique_ptr<Texture>> textureCache;
std::unordered_map<std::string, std::unique_ptr<Texture>> previewTextureCacheLinear;
std::unordered_map<std::string, std::unique_ptr<Texture>> previewTextureCacheNearest;
struct ShaderEntry { struct ShaderEntry {
std::unique_ptr<Shader> shader; std::unique_ptr<Shader> shader;
fs::file_time_type vertTime; fs::file_time_type vertTime;
@@ -131,6 +133,7 @@ public:
void initialize(); void initialize();
Texture* getTexture(const std::string& path); Texture* getTexture(const std::string& path);
Texture* getTexturePreview(const std::string& path, bool nearest);
Shader* getShader(const std::string& vert, const std::string& frag); Shader* getShader(const std::string& vert, const std::string& frag);
bool forceReloadShader(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; } void setAmbientColor(const glm::vec3& color) { ambientColor = color; }

View File

@@ -122,6 +122,43 @@ bool ScriptContext::GetRigidbodyVelocity(glm::vec3& outVelocity) const {
return engine->getRigidbodyVelocityFromScript(object->id, outVelocity); 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) { bool ScriptContext::TeleportRigidbody(const glm::vec3& pos, const glm::vec3& rotDeg) {
if (!engine || !object) return false; if (!engine || !object) return false;
object->position = pos; object->position = pos;

View File

@@ -39,6 +39,13 @@ struct ScriptContext {
bool HasRigidbody() const; bool HasRigidbody() const;
bool SetRigidbodyVelocity(const glm::vec3& velocity); bool SetRigidbodyVelocity(const glm::vec3& velocity);
bool GetRigidbodyVelocity(glm::vec3& outVelocity) const; 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); bool TeleportRigidbody(const glm::vec3& pos, const glm::vec3& rotDeg);
// Audio helpers // Audio helpers
bool HasAudioSource() const; bool HasAudioSource() const;