part 2 since i forgot to stage changes lol.
This commit is contained in:
@@ -80,6 +80,7 @@ bool AudioSystem::ensureSoundFor(const SceneObject& obj) {
|
|||||||
|
|
||||||
snd.clipPath = obj.audioSource.clipPath;
|
snd.clipPath = obj.audioSource.clipPath;
|
||||||
snd.spatial = obj.audioSource.spatial;
|
snd.spatial = obj.audioSource.spatial;
|
||||||
|
snd.started = false;
|
||||||
refreshSoundParams(obj, snd);
|
refreshSoundParams(obj, snd);
|
||||||
activeSounds[obj.id] = std::move(snd);
|
activeSounds[obj.id] = std::move(snd);
|
||||||
return true;
|
return true;
|
||||||
@@ -93,8 +94,9 @@ void AudioSystem::refreshSoundParams(const SceneObject& obj, ActiveSound& snd) {
|
|||||||
ma_sound_set_max_distance(&snd.sound, obj.audioSource.maxDistance);
|
ma_sound_set_max_distance(&snd.sound, obj.audioSource.maxDistance);
|
||||||
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) && obj.audioSource.playOnStart && obj.audioSource.enabled) {
|
if (!ma_sound_is_playing(&snd.sound) && !snd.started && obj.audioSource.playOnStart && obj.audioSource.enabled) {
|
||||||
ma_sound_start(&snd.sound);
|
ma_sound_start(&snd.sound);
|
||||||
|
snd.started = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +199,34 @@ bool AudioSystem::seekPreview(const std::string& path, double seconds) {
|
|||||||
return res == MA_SUCCESS;
|
return res == MA_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
snd.started = true;
|
||||||
|
return ma_sound_start(&snd.sound) == MA_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioSystem::setObjectLoop(const SceneObject& obj, bool loop) {
|
||||||
|
if (!ensureSoundFor(obj)) return false;
|
||||||
|
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];
|
||||||
|
ma_sound_set_volume(&snd.sound, volume);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
AudioClipPreview AudioSystem::loadPreview(const std::string& path) {
|
AudioClipPreview AudioSystem::loadPreview(const std::string& path) {
|
||||||
AudioClipPreview preview;
|
AudioClipPreview preview;
|
||||||
preview.path = path;
|
preview.path = path;
|
||||||
|
|||||||
@@ -33,11 +33,18 @@ public:
|
|||||||
bool getPreviewTime(const std::string& path, double& cursorSeconds, double& durationSeconds) const;
|
bool getPreviewTime(const std::string& path, double& cursorSeconds, double& durationSeconds) const;
|
||||||
bool seekPreview(const std::string& path, double seconds);
|
bool seekPreview(const std::string& path, double seconds);
|
||||||
|
|
||||||
|
// Scene audio control (runtime)
|
||||||
|
bool playObjectSound(const SceneObject& obj);
|
||||||
|
bool stopObjectSound(int objectId);
|
||||||
|
bool setObjectLoop(const SceneObject& obj, bool loop);
|
||||||
|
bool setObjectVolume(const SceneObject& obj, float volume);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ActiveSound {
|
struct ActiveSound {
|
||||||
ma_sound sound;
|
ma_sound sound;
|
||||||
std::string clipPath;
|
std::string clipPath;
|
||||||
bool spatial = true;
|
bool spatial = true;
|
||||||
|
bool started = false; // prevents auto-restart after manual stop
|
||||||
};
|
};
|
||||||
|
|
||||||
ma_engine engine{};
|
ma_engine engine{};
|
||||||
|
|||||||
@@ -1266,6 +1266,42 @@ bool Engine::teleportPhysicsActorFromScript(int id, const glm::vec3& position, c
|
|||||||
return physics.setActorPose(id, position, rotationDeg);
|
return physics.setActorPose(id, position, rotationDeg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Engine::playAudioFromScript(int id) {
|
||||||
|
SceneObject* obj = findObjectById(id);
|
||||||
|
if (!obj || !obj->hasAudioSource) return false;
|
||||||
|
return audio.playObjectSound(*obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Engine::stopAudioFromScript(int id) {
|
||||||
|
return audio.stopObjectSound(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Engine::setAudioLoopFromScript(int id, bool loop) {
|
||||||
|
SceneObject* obj = findObjectById(id);
|
||||||
|
if (!obj || !obj->hasAudioSource) return false;
|
||||||
|
obj->audioSource.loop = loop;
|
||||||
|
markProjectDirty();
|
||||||
|
return audio.setObjectLoop(*obj, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Engine::setAudioVolumeFromScript(int id, float volume) {
|
||||||
|
SceneObject* obj = findObjectById(id);
|
||||||
|
if (!obj || !obj->hasAudioSource) return false;
|
||||||
|
obj->audioSource.volume = std::clamp(volume, 0.0f, 2.0f);
|
||||||
|
markProjectDirty();
|
||||||
|
return audio.setObjectVolume(*obj, obj->audioSource.volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Engine::setAudioClipFromScript(int id, const std::string& path) {
|
||||||
|
SceneObject* obj = findObjectById(id);
|
||||||
|
if (!obj || !obj->hasAudioSource) return false;
|
||||||
|
obj->audioSource.clipPath = path;
|
||||||
|
markProjectDirty();
|
||||||
|
// Ensure clip is loaded; do not auto-play unless PlayAudio is called.
|
||||||
|
audio.setObjectLoop(*obj, obj->audioSource.loop);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::compileScriptFile(const fs::path& scriptPath) {
|
void Engine::compileScriptFile(const fs::path& scriptPath) {
|
||||||
if (!projectManager.currentProject.isLoaded) {
|
if (!projectManager.currentProject.isLoaded) {
|
||||||
addConsoleMessage("No project is loaded", ConsoleMessageType::Warning);
|
addConsoleMessage("No project is loaded", ConsoleMessageType::Warning);
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ private:
|
|||||||
bool isPlaying = false;
|
bool isPlaying = false;
|
||||||
bool isPaused = false;
|
bool isPaused = false;
|
||||||
bool showViewOutput = true;
|
bool showViewOutput = true;
|
||||||
|
bool showSceneGizmos = true;
|
||||||
bool showGameViewport = true;
|
bool showGameViewport = true;
|
||||||
int previewCameraId = -1;
|
int previewCameraId = -1;
|
||||||
bool gameViewCursorLocked = false;
|
bool gameViewCursorLocked = false;
|
||||||
@@ -221,4 +222,10 @@ public:
|
|||||||
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 teleportPhysicsActorFromScript(int id, const glm::vec3& position, const glm::vec3& rotationDeg);
|
bool teleportPhysicsActorFromScript(int id, const glm::vec3& position, const glm::vec3& rotationDeg);
|
||||||
|
// Audio control exposed to scripts
|
||||||
|
bool playAudioFromScript(int id);
|
||||||
|
bool stopAudioFromScript(int id);
|
||||||
|
bool setAudioLoopFromScript(int id, bool loop);
|
||||||
|
bool setAudioVolumeFromScript(int id, float volume);
|
||||||
|
bool setAudioClipFromScript(int id, const std::string& path);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3576,6 +3576,7 @@ void Engine::renderViewport() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool mouseOverViewportImage = false;
|
bool mouseOverViewportImage = false;
|
||||||
|
bool blockSelection = false;
|
||||||
|
|
||||||
if (rendererInitialized) {
|
if (rendererInitialized) {
|
||||||
glm::mat4 proj = glm::perspective(
|
glm::mat4 proj = glm::perspective(
|
||||||
@@ -3687,6 +3688,11 @@ void Engine::renderViewport() {
|
|||||||
if (clickedIdx >= 0) {
|
if (clickedIdx >= 0) {
|
||||||
setCameraFacing(arrows[clickedIdx].dir);
|
setCameraFacing(arrows[clickedIdx].dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent viewport picking when interacting with the axis widget.
|
||||||
|
if (widgetHover) {
|
||||||
|
blockSelection = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto projectToScreen = [&](const glm::vec3& p) -> std::optional<ImVec2> {
|
auto projectToScreen = [&](const glm::vec3& p) -> std::optional<ImVec2> {
|
||||||
@@ -4170,9 +4176,11 @@ void Engine::renderViewport() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& obj : sceneObjects) {
|
if (showSceneGizmos) {
|
||||||
if (obj.type == ObjectType::Camera) {
|
for (const auto& obj : sceneObjects) {
|
||||||
drawCameraDirection(obj);
|
if (obj.type == ObjectType::Camera) {
|
||||||
|
drawCameraDirection(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4269,9 +4277,11 @@ void Engine::renderViewport() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& obj : sceneObjects) {
|
if (showSceneGizmos) {
|
||||||
if (obj.type == ObjectType::PointLight || obj.type == ObjectType::SpotLight || obj.type == ObjectType::AreaLight) {
|
for (const auto& obj : sceneObjects) {
|
||||||
drawLightOverlays(obj);
|
if (obj.type == ObjectType::PointLight || obj.type == ObjectType::SpotLight || obj.type == ObjectType::AreaLight) {
|
||||||
|
drawLightOverlays(obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4406,6 +4416,14 @@ void Engine::renderViewport() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine(0.0f, toolbarSpacing * 1.25f);
|
||||||
|
if (GizmoToolbar::ModeButton("Gizmos", showSceneGizmos, ImVec2(70, 24), baseCol, accentCol, textCol)) {
|
||||||
|
showSceneGizmos = !showSceneGizmos;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Toggle light/camera scene symbols");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
@@ -4420,10 +4438,16 @@ void Engine::renderViewport() {
|
|||||||
|
|
||||||
splitter.Merge(toolbarDrawList);
|
splitter.Merge(toolbarDrawList);
|
||||||
|
|
||||||
|
// Prevent viewport picking when clicking on the toolbar overlay.
|
||||||
|
if (ImGui::IsMouseHoveringRect(bgMin, bgMax)) {
|
||||||
|
blockSelection = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Left-click picking inside viewport
|
// Left-click picking inside viewport
|
||||||
if (mouseOverViewportImage &&
|
if (mouseOverViewportImage &&
|
||||||
ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
|
ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
|
||||||
!ImGuizmo::IsUsing() && !ImGuizmo::IsOver())
|
!ImGuizmo::IsUsing() && !ImGuizmo::IsOver() &&
|
||||||
|
!blockSelection)
|
||||||
{
|
{
|
||||||
glm::mat4 invViewProj = glm::inverse(proj * view);
|
glm::mat4 invViewProj = glm::inverse(proj * view);
|
||||||
ImVec2 mousePos = ImGui::GetMousePos();
|
ImVec2 mousePos = ImGui::GetMousePos();
|
||||||
|
|||||||
@@ -108,6 +108,42 @@ bool ScriptContext::TeleportRigidbody(const glm::vec3& pos, const glm::vec3& rot
|
|||||||
return engine->teleportPhysicsActorFromScript(object->id, pos, object->rotation);
|
return engine->teleportPhysicsActorFromScript(object->id, pos, object->rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::HasAudioSource() const {
|
||||||
|
return object && object->hasAudioSource && object->audioSource.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::PlayAudio() {
|
||||||
|
if (!engine || !object || !object->hasAudioSource) return false;
|
||||||
|
return engine->playAudioFromScript(object->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::StopAudio() {
|
||||||
|
if (!engine || !object || !object->hasAudioSource) return false;
|
||||||
|
return engine->stopAudioFromScript(object->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::SetAudioLoop(bool loop) {
|
||||||
|
if (!engine || !object || !object->hasAudioSource) return false;
|
||||||
|
object->audioSource.loop = loop;
|
||||||
|
engine->markProjectDirty();
|
||||||
|
return engine->setAudioLoopFromScript(object->id, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::SetAudioVolume(float volume) {
|
||||||
|
if (!engine || !object || !object->hasAudioSource) return false;
|
||||||
|
float clamped = std::clamp(volume, 0.0f, 2.0f);
|
||||||
|
object->audioSource.volume = clamped;
|
||||||
|
engine->markProjectDirty();
|
||||||
|
return engine->setAudioVolumeFromScript(object->id, clamped);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptContext::SetAudioClip(const std::string& path) {
|
||||||
|
if (!engine || !object || !object->hasAudioSource) return false;
|
||||||
|
object->audioSource.clipPath = path;
|
||||||
|
engine->markProjectDirty();
|
||||||
|
return engine->setAudioClipFromScript(object->id, path);
|
||||||
|
}
|
||||||
|
|
||||||
std::string ScriptContext::GetSetting(const std::string& key, const std::string& fallback) const {
|
std::string ScriptContext::GetSetting(const std::string& key, const std::string& fallback) const {
|
||||||
if (!script) return fallback;
|
if (!script) return fallback;
|
||||||
auto it = std::find_if(script->settings.begin(), script->settings.end(),
|
auto it = std::find_if(script->settings.begin(), script->settings.end(),
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ struct ScriptContext {
|
|||||||
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 TeleportRigidbody(const glm::vec3& pos, const glm::vec3& rotDeg);
|
bool TeleportRigidbody(const glm::vec3& pos, const glm::vec3& rotDeg);
|
||||||
|
// Audio helpers
|
||||||
|
bool HasAudioSource() const;
|
||||||
|
bool PlayAudio();
|
||||||
|
bool StopAudio();
|
||||||
|
bool SetAudioLoop(bool loop);
|
||||||
|
bool SetAudioVolume(float volume);
|
||||||
|
bool SetAudioClip(const std::string& path);
|
||||||
// Settings helpers (auto-mark dirty)
|
// Settings helpers (auto-mark dirty)
|
||||||
std::string GetSetting(const std::string& key, const std::string& fallback = "") const;
|
std::string GetSetting(const std::string& key, const std::string& fallback = "") const;
|
||||||
void SetSetting(const std::string& key, const std::string& value);
|
void SetSetting(const std::string& key, const std::string& value);
|
||||||
|
|||||||
Reference in New Issue
Block a user