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

@@ -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<SceneObject>& 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<ActiveSound>();
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<SceneObject>& 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;
}

View File

@@ -49,8 +49,9 @@ private:
ma_engine engine{};
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_set<std::string> missingClips;
ma_sound previewSound{};
bool previewActive = false;

View File

@@ -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<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 (browserHasMaterial) {
renderMaterialAssetPanel("Material Asset", true);
} else if (browserHasAudio) {
renderAudioAssetPanel("Audio Clip", nullptr);
} else if (browserHasTexture) {
renderTextureAssetPanel("Texture");
} else {
ImGui::TextDisabled("No object selected");
}

View File

@@ -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;

View File

@@ -13,6 +13,7 @@
#include "AudioSystem.h"
#include "PackageManager.h"
#include "../include/Window/Window.h"
#include <unordered_map>
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<std::string, bool> 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);

View File

@@ -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<PxRigidDynamic>()) {
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<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) {
#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<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,
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<SceneObject>&) {}
void PhysicsSystem::onPlayStop() {}
void PhysicsSystem::simulate(float, std::vector<SceneObject>&) {}

View File

@@ -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;

View File

@@ -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<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() {
shader = new Shader(defaultVertPath.c_str(), defaultFragPath.c_str());
defaultShader = shader;

View File

@@ -86,6 +86,8 @@ private:
Texture* texture1 = nullptr;
Texture* texture2 = nullptr;
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 {
std::unique_ptr<Shader> 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; }

View File

@@ -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;

View File

@@ -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;