Editor/plugin separation - closes #844
Compilable "no-studio" option - closes #845
This commit is contained in:
parent
e77425eb37
commit
6a108bbbb1
16 changed files with 1228 additions and 1620 deletions
|
@ -368,6 +368,11 @@ solution "LumixEngine"
|
|||
BINARY_DIR = LOCATION .. "/bin/"
|
||||
end
|
||||
|
||||
if not build_studio then
|
||||
excludes { "../src/**/editor/*" }
|
||||
end
|
||||
|
||||
|
||||
configurations { "Debug", "Release", "RelWithDebInfo" }
|
||||
platforms { "x32", "x64" }
|
||||
flags { "FatalWarnings", "NoPCH" }
|
||||
|
@ -545,12 +550,14 @@ if build_app then
|
|||
forceLink("s_physics_plugin_register")
|
||||
forceLink("s_renderer_plugin_register")
|
||||
|
||||
forceLink("setStudioApp_animation")
|
||||
forceLink("setStudioApp_audio")
|
||||
forceLink("setStudioApp_lua_script")
|
||||
forceLink("setStudioApp_physics")
|
||||
forceLink("setStudioApp_renderer")
|
||||
|
||||
if build_studio then
|
||||
forceLink("setStudioApp_animation")
|
||||
forceLink("setStudioApp_audio")
|
||||
forceLink("setStudioApp_lua_script")
|
||||
forceLink("setStudioApp_physics")
|
||||
forceLink("setStudioApp_renderer")
|
||||
end
|
||||
|
||||
links { "engine", "audio", "animation", "renderer", "lua_script", "navigation" }
|
||||
|
||||
if build_physics then
|
||||
|
|
|
@ -6,18 +6,14 @@
|
|||
#include "engine/json_serializer.h"
|
||||
#include "engine/profiler.h"
|
||||
#include "engine/resource_manager.h"
|
||||
#include "editor/asset_browser.h"
|
||||
#include "editor/property_grid.h"
|
||||
#include "editor/studio_app.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/property_descriptor.h"
|
||||
#include "engine/property_register.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "renderer/model.h"
|
||||
#include "renderer/pose.h"
|
||||
#include "renderer/render_scene.h"
|
||||
#include "engine/universe/universe.h"
|
||||
#include <cfloat>
|
||||
|
||||
|
||||
namespace Lumix
|
||||
|
@ -45,7 +41,7 @@ enum class AnimationSceneVersion : int
|
|||
};
|
||||
|
||||
|
||||
struct AnimationSceneImpl : public IScene
|
||||
struct AnimationSceneImpl : public AnimationScene
|
||||
{
|
||||
friend struct AnimationSystemImpl;
|
||||
|
||||
|
@ -95,6 +91,24 @@ struct AnimationSceneImpl : public IScene
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
float getAnimableTime(ComponentIndex cmp) override
|
||||
{
|
||||
return m_animables[cmp].time;
|
||||
}
|
||||
|
||||
|
||||
void setAnimableTime(ComponentIndex cmp, float time) override
|
||||
{
|
||||
m_animables[cmp].time = time;
|
||||
}
|
||||
|
||||
|
||||
Animation* getAnimableAnimation(ComponentIndex cmp) override
|
||||
{
|
||||
return m_animables[cmp].animation;
|
||||
}
|
||||
|
||||
|
||||
void startGame() override { m_is_game_running = true; }
|
||||
void stopGame() override { m_is_game_running = false; }
|
||||
|
@ -228,7 +242,7 @@ struct AnimationSceneImpl : public IScene
|
|||
}
|
||||
|
||||
|
||||
void updateAnimable(ComponentIndex cmp, float time_delta)
|
||||
void updateAnimable(ComponentIndex cmp, float time_delta) override
|
||||
{
|
||||
Animable& animable = m_animables[cmp];
|
||||
if ((animable.flags & Animable::FREE) != 0) return;
|
||||
|
@ -263,7 +277,7 @@ struct AnimationSceneImpl : public IScene
|
|||
|
||||
for (int i = 0, c = m_animables.size(); i < c; ++i)
|
||||
{
|
||||
updateAnimable(i, time_delta);
|
||||
AnimationSceneImpl::updateAnimable(i, time_delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -378,105 +392,6 @@ private:
|
|||
};
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
struct AssetBrowserPlugin : AssetBrowser::IPlugin
|
||||
{
|
||||
|
||||
explicit AssetBrowserPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
|
||||
{
|
||||
if (type == ResourceManager::ANIMATION)
|
||||
{
|
||||
auto* animation = static_cast<Animation*>(resource);
|
||||
ImGui::LabelText("FPS", "%d", animation->getFPS());
|
||||
ImGui::LabelText("Length", "%.3fs", animation->getLength());
|
||||
ImGui::LabelText("Frames", "%d", animation->getFrameCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void onResourceUnloaded(Resource* resource) override {}
|
||||
|
||||
|
||||
const char* getName() const override { return "Animation"; }
|
||||
|
||||
|
||||
bool hasResourceManager(uint32 type) const override { return type == ResourceManager::ANIMATION; }
|
||||
|
||||
|
||||
uint32 getResourceType(const char* ext) override
|
||||
{
|
||||
if (equalStrings(ext, "ani")) return ResourceManager::ANIMATION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
};
|
||||
|
||||
|
||||
struct PropertyGridPlugin : PropertyGrid::IPlugin
|
||||
{
|
||||
explicit PropertyGridPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
m_is_playing = false;
|
||||
}
|
||||
|
||||
|
||||
void onGUI(PropertyGrid& grid, Lumix::ComponentUID cmp) override
|
||||
{
|
||||
if (cmp.type != ANIMABLE_HASH) return;
|
||||
|
||||
auto* scene = static_cast<AnimationSceneImpl*>(cmp.scene);
|
||||
auto& animable = scene->m_animables[cmp.index];
|
||||
if (!animable.animation) return;
|
||||
if (!animable.animation->isReady()) return;
|
||||
|
||||
ImGui::Checkbox("Preview", &m_is_playing);
|
||||
if (ImGui::SliderFloat("Time", &animable.time, 0, animable.animation->getLength()))
|
||||
{
|
||||
scene->updateAnimable(cmp.index, 0);
|
||||
}
|
||||
|
||||
if (m_is_playing)
|
||||
{
|
||||
float time_delta = m_app.getWorldEditor()->getEngine().getLastTimeDelta();
|
||||
scene->updateAnimable(cmp.index, time_delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
bool m_is_playing;
|
||||
};
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(animation)
|
||||
{
|
||||
auto& allocator = app.getWorldEditor()->getAllocator();
|
||||
auto* ab_plugin = LUMIX_NEW(allocator, AssetBrowserPlugin)(app);
|
||||
app.getAssetBrowser()->addPlugin(*ab_plugin);
|
||||
|
||||
auto* pg_plugin = LUMIX_NEW(allocator, PropertyGridPlugin)(app);
|
||||
app.getPropertyGrid()->addPlugin(*pg_plugin);
|
||||
}
|
||||
|
||||
|
||||
LUMIX_PLUGIN_ENTRY(animation)
|
||||
{
|
||||
return LUMIX_NEW(engine.getAllocator(), AnimationSystemImpl)(engine);
|
||||
|
|
|
@ -8,8 +8,15 @@
|
|||
namespace Lumix
|
||||
{
|
||||
|
||||
extern "C" {
|
||||
LUMIX_ANIMATION_API IPlugin* createPlugin(Engine& engine);
|
||||
}
|
||||
|
||||
class AnimationScene : public IScene
|
||||
{
|
||||
public:
|
||||
virtual class Animation* getAnimableAnimation(ComponentIndex cmp) = 0;
|
||||
virtual float getAnimableTime(ComponentIndex cmp) = 0;
|
||||
virtual void setAnimableTime(ComponentIndex cmp, float time) = 0;
|
||||
virtual void updateAnimable(ComponentIndex cmp, float time_delta) = 0;
|
||||
};
|
||||
|
||||
|
||||
} // ~ namespace Lumix
|
||||
|
|
118
src/animation/editor/plugins.cpp
Normal file
118
src/animation/editor/plugins.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include "animation/animation_system.h"
|
||||
#include "animation/animation.h"
|
||||
#include "engine/crc32.h"
|
||||
#include "engine/resource_manager.h"
|
||||
#include "editor/asset_browser.h"
|
||||
#include "editor/property_grid.h"
|
||||
#include "editor/studio_app.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
|
||||
using namespace Lumix;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
static const uint32 ANIMABLE_HASH = crc32("animable");
|
||||
|
||||
|
||||
struct AssetBrowserPlugin : AssetBrowser::IPlugin
|
||||
{
|
||||
|
||||
explicit AssetBrowserPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
|
||||
{
|
||||
if (type == ResourceManager::ANIMATION)
|
||||
{
|
||||
auto* animation = static_cast<Animation*>(resource);
|
||||
ImGui::LabelText("FPS", "%d", animation->getFPS());
|
||||
ImGui::LabelText("Length", "%.3fs", animation->getLength());
|
||||
ImGui::LabelText("Frames", "%d", animation->getFrameCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void onResourceUnloaded(Resource* resource) override {}
|
||||
|
||||
|
||||
const char* getName() const override { return "Animation"; }
|
||||
|
||||
|
||||
bool hasResourceManager(uint32 type) const override { return type == ResourceManager::ANIMATION; }
|
||||
|
||||
|
||||
uint32 getResourceType(const char* ext) override
|
||||
{
|
||||
if (equalStrings(ext, "ani")) return ResourceManager::ANIMATION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
};
|
||||
|
||||
|
||||
struct PropertyGridPlugin : PropertyGrid::IPlugin
|
||||
{
|
||||
explicit PropertyGridPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
m_is_playing = false;
|
||||
}
|
||||
|
||||
|
||||
void onGUI(PropertyGrid& grid, Lumix::ComponentUID cmp) override
|
||||
{
|
||||
if (cmp.type != ANIMABLE_HASH) return;
|
||||
|
||||
auto* scene = static_cast<AnimationScene*>(cmp.scene);
|
||||
auto* animation = scene->getAnimableAnimation(cmp.index);
|
||||
if (!animation) return;
|
||||
if (!animation->isReady()) return;
|
||||
|
||||
ImGui::Checkbox("Preview", &m_is_playing);
|
||||
float time = scene->getAnimableTime(cmp.index);
|
||||
if (ImGui::SliderFloat("Time", &time, 0, animation->getLength()))
|
||||
{
|
||||
scene->setAnimableTime(cmp.index, time);
|
||||
scene->updateAnimable(cmp.index, 0);
|
||||
}
|
||||
|
||||
if (m_is_playing)
|
||||
{
|
||||
float time_delta = m_app.getWorldEditor()->getEngine().getLastTimeDelta();
|
||||
scene->updateAnimable(cmp.index, time_delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
bool m_is_playing;
|
||||
};
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(animation)
|
||||
{
|
||||
auto& allocator = app.getWorldEditor()->getAllocator();
|
||||
auto* ab_plugin = LUMIX_NEW(allocator, AssetBrowserPlugin)(app);
|
||||
app.getAssetBrowser()->addPlugin(*ab_plugin);
|
||||
|
||||
auto* pg_plugin = LUMIX_NEW(allocator, PropertyGridPlugin)(app);
|
||||
app.getPropertyGrid()->addPlugin(*pg_plugin);
|
||||
}
|
||||
|
|
@ -17,8 +17,6 @@
|
|||
#include "engine/system.h"
|
||||
#include "engine/timer.h"
|
||||
#include "engine/debug/debug.h"
|
||||
#include "editor/gizmo.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/plugin_manager.h"
|
||||
#include "renderer/pipeline.h"
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "engine/matrix.h"
|
||||
#include "engine/resource_manager.h"
|
||||
#include "engine/resource_manager_base.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "lua_script/lua_script_system.h"
|
||||
#include "engine/universe/universe.h"
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include "engine/plugin_manager.h"
|
||||
#include "engine/property_register.h"
|
||||
#include "engine/property_descriptor.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "renderer/render_scene.h"
|
||||
|
||||
|
||||
|
@ -115,233 +114,6 @@ struct AudioSystemImpl : public AudioSystem
|
|||
};
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
struct AssetBrowserPlugin : public AssetBrowser::IPlugin
|
||||
{
|
||||
explicit AssetBrowserPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
, m_browser(*app.getAssetBrowser())
|
||||
, m_playing_clip(-1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Lumix::AudioDevice& getAudioDevice(Lumix::Engine& engine)
|
||||
{
|
||||
auto* audio = static_cast<Lumix::AudioSystem*>(engine.getPluginManager().getPlugin("audio"));
|
||||
return audio->getDevice();
|
||||
}
|
||||
|
||||
|
||||
void stopAudio()
|
||||
{
|
||||
if (m_playing_clip < 0) return;
|
||||
|
||||
getAudioDevice(m_app.getWorldEditor()->getEngine()).stop(m_playing_clip);
|
||||
m_playing_clip = -1;
|
||||
}
|
||||
|
||||
|
||||
const char* getName() const override
|
||||
{
|
||||
return "Audio";
|
||||
}
|
||||
|
||||
|
||||
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
|
||||
{
|
||||
if (type != CLIP_HASH) return false;
|
||||
auto* clip = static_cast<Lumix::Clip*>(resource);
|
||||
ImGui::LabelText("Length", "%f", clip->getLengthSeconds());
|
||||
auto& device = getAudioDevice(m_app.getWorldEditor()->getEngine());
|
||||
|
||||
if (m_playing_clip >= 0)
|
||||
{
|
||||
if (ImGui::Button("Stop"))
|
||||
{
|
||||
stopAudio();
|
||||
return true;
|
||||
}
|
||||
float time = device.getCurrentTime(m_playing_clip);
|
||||
if (ImGui::SliderFloat("Time", &time, 0, clip->getLengthSeconds(), "%.2fs"))
|
||||
{
|
||||
device.setCurrentTime(m_playing_clip, time);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_playing_clip < 0 && ImGui::Button("Play"))
|
||||
{
|
||||
stopAudio();
|
||||
|
||||
auto handle = device.createBuffer(clip->getData(),
|
||||
clip->getSize(),
|
||||
clip->getChannels(),
|
||||
clip->getSampleRate(),
|
||||
0);
|
||||
device.play(handle, false);
|
||||
m_playing_clip = handle;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void onResourceUnloaded(Lumix::Resource*) override
|
||||
{
|
||||
stopAudio();
|
||||
}
|
||||
|
||||
|
||||
bool hasResourceManager(Lumix::uint32 type) const override
|
||||
{
|
||||
return type == CLIP_HASH;
|
||||
}
|
||||
|
||||
|
||||
Lumix::uint32 getResourceType(const char* ext) override
|
||||
{
|
||||
if (Lumix::equalStrings(ext, "ogg")) return CLIP_HASH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int m_playing_clip;
|
||||
StudioApp& m_app;
|
||||
AssetBrowser& m_browser;
|
||||
};
|
||||
|
||||
|
||||
struct StudioAppPlugin : public StudioApp::IPlugin
|
||||
{
|
||||
explicit StudioAppPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
m_filter[0] = 0;
|
||||
m_is_opened = false;
|
||||
m_action = LUMIX_NEW(app.getWorldEditor()->getAllocator(), Action)("Clip manager", "clip_manager");
|
||||
m_action->func.bind<StudioAppPlugin, &StudioAppPlugin::onAction>(this);
|
||||
}
|
||||
|
||||
|
||||
void onAction()
|
||||
{
|
||||
m_is_opened = !m_is_opened;
|
||||
}
|
||||
|
||||
|
||||
void onWindowGUI() override
|
||||
{
|
||||
if (ImGui::BeginDock("Clip Manager", &m_is_opened))
|
||||
{
|
||||
ImGui::InputText("Filter", m_filter, Lumix::lengthOf(m_filter));
|
||||
|
||||
auto* audio_scene =
|
||||
static_cast<Lumix::AudioScene*>(m_app.getWorldEditor()->getScene(Lumix::crc32("audio")));
|
||||
int clip_count = audio_scene->getClipCount();
|
||||
for (int clip_id = 0; clip_id < clip_count; ++clip_id)
|
||||
{
|
||||
auto* clip_info = audio_scene->getClipInfo(clip_id);
|
||||
|
||||
if (m_filter[0] != 0 && Lumix::stristr(clip_info->name, m_filter) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode((const void*)(uintptr)clip_id, "%s", clip_info->name))
|
||||
{
|
||||
char buf[30];
|
||||
Lumix::copyString(buf, Lumix::lengthOf(buf), clip_info->name);
|
||||
if (ImGui::InputText("Name", buf, sizeof(buf)))
|
||||
{
|
||||
Lumix::copyString(clip_info->name, buf);
|
||||
clip_info->name_hash = Lumix::crc32(buf);
|
||||
}
|
||||
auto* clip = audio_scene->getClipInfo(clip_id)->clip;
|
||||
char path[Lumix::MAX_PATH_LENGTH];
|
||||
Lumix::copyString(path, clip ? clip->getPath().c_str() : "");
|
||||
if (m_app.getAssetBrowser()->resourceInput(
|
||||
"Clip", "", path, Lumix::lengthOf(path), CLIP_HASH))
|
||||
{
|
||||
audio_scene->setClip(clip_id, Lumix::Path(path));
|
||||
}
|
||||
bool looped = audio_scene->getClipInfo(clip_id)->looped;
|
||||
if (ImGui::Checkbox("Looped", &looped))
|
||||
{
|
||||
clip_info->looped = looped;
|
||||
}
|
||||
if (ImGui::Button("Remove"))
|
||||
{
|
||||
audio_scene->removeClip(clip_info);
|
||||
--clip_count;
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Button("Add"))
|
||||
{
|
||||
audio_scene->addClip("test", Lumix::Path("test.ogg"));
|
||||
}
|
||||
}
|
||||
ImGui::EndDock();
|
||||
}
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
char m_filter[256];
|
||||
bool m_is_opened;
|
||||
};
|
||||
|
||||
|
||||
struct EditorPlugin : public WorldEditor::Plugin
|
||||
{
|
||||
explicit EditorPlugin(WorldEditor& editor)
|
||||
: m_editor(editor)
|
||||
{
|
||||
}
|
||||
|
||||
bool showGizmo(ComponentUID cmp) override
|
||||
{
|
||||
static const uint32 ECHO_ZONE_HASH = crc32("echo_zone");
|
||||
|
||||
if (cmp.type == ECHO_ZONE_HASH)
|
||||
{
|
||||
auto* audio_scene = static_cast<AudioScene*>(cmp.scene);
|
||||
float radius = audio_scene->getEchoZoneRadius(cmp.index);
|
||||
Universe& universe = audio_scene->getUniverse();
|
||||
Vec3 pos = universe.getPosition(cmp.entity);
|
||||
|
||||
auto* scene = static_cast<RenderScene*>(m_editor.getScene(crc32("renderer")));
|
||||
if (!scene) return true;
|
||||
scene->addDebugSphere(pos, radius, 0xff0000ff, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldEditor& m_editor;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(audio)
|
||||
{
|
||||
auto& editor = *app.getWorldEditor();
|
||||
auto& allocator = editor.getAllocator();
|
||||
|
||||
auto* asset_browser_plugin = LUMIX_NEW(allocator, AssetBrowserPlugin)(app);
|
||||
app.getAssetBrowser()->addPlugin(*asset_browser_plugin);
|
||||
|
||||
auto* app_plugin = LUMIX_NEW(allocator, StudioAppPlugin)(app);
|
||||
app.addPlugin(*app_plugin);
|
||||
|
||||
auto* plugin = LUMIX_NEW(allocator, EditorPlugin)(editor);
|
||||
editor.addPlugin(*plugin);
|
||||
}
|
||||
|
||||
|
||||
LUMIX_PLUGIN_ENTRY(audio)
|
||||
{
|
||||
return LUMIX_NEW(engine.getAllocator(), Lumix::AudioSystemImpl)(engine);
|
||||
|
|
232
src/audio/editor/plugins.cpp
Normal file
232
src/audio/editor/plugins.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
#include "audio_system.h"
|
||||
#include "audio_device.h"
|
||||
#include "audio_scene.h"
|
||||
#include "clip_manager.h"
|
||||
#include "editor/asset_browser.h"
|
||||
#include "editor/studio_app.h"
|
||||
#include "editor/utils.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/crc32.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/plugin_manager.h"
|
||||
#include "engine/universe/universe.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "renderer/render_scene.h"
|
||||
|
||||
|
||||
using namespace Lumix;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
static const Lumix::uint32 CLIP_HASH = Lumix::crc32("CLIP");
|
||||
|
||||
|
||||
struct AssetBrowserPlugin : public AssetBrowser::IPlugin
|
||||
{
|
||||
explicit AssetBrowserPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
, m_browser(*app.getAssetBrowser())
|
||||
, m_playing_clip(-1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Lumix::AudioDevice& getAudioDevice(Lumix::Engine& engine)
|
||||
{
|
||||
auto* audio = static_cast<Lumix::AudioSystem*>(engine.getPluginManager().getPlugin("audio"));
|
||||
return audio->getDevice();
|
||||
}
|
||||
|
||||
|
||||
void stopAudio()
|
||||
{
|
||||
if (m_playing_clip < 0) return;
|
||||
|
||||
getAudioDevice(m_app.getWorldEditor()->getEngine()).stop(m_playing_clip);
|
||||
m_playing_clip = -1;
|
||||
}
|
||||
|
||||
|
||||
const char* getName() const override { return "Audio"; }
|
||||
|
||||
|
||||
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
|
||||
{
|
||||
if (type != CLIP_HASH) return false;
|
||||
auto* clip = static_cast<Lumix::Clip*>(resource);
|
||||
ImGui::LabelText("Length", "%f", clip->getLengthSeconds());
|
||||
auto& device = getAudioDevice(m_app.getWorldEditor()->getEngine());
|
||||
|
||||
if (m_playing_clip >= 0)
|
||||
{
|
||||
if (ImGui::Button("Stop"))
|
||||
{
|
||||
stopAudio();
|
||||
return true;
|
||||
}
|
||||
float time = device.getCurrentTime(m_playing_clip);
|
||||
if (ImGui::SliderFloat("Time", &time, 0, clip->getLengthSeconds(), "%.2fs"))
|
||||
{
|
||||
device.setCurrentTime(m_playing_clip, time);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_playing_clip < 0 && ImGui::Button("Play"))
|
||||
{
|
||||
stopAudio();
|
||||
|
||||
auto handle =
|
||||
device.createBuffer(clip->getData(), clip->getSize(), clip->getChannels(), clip->getSampleRate(), 0);
|
||||
device.play(handle, false);
|
||||
m_playing_clip = handle;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void onResourceUnloaded(Lumix::Resource*) override { stopAudio(); }
|
||||
|
||||
|
||||
bool hasResourceManager(Lumix::uint32 type) const override { return type == CLIP_HASH; }
|
||||
|
||||
|
||||
Lumix::uint32 getResourceType(const char* ext) override
|
||||
{
|
||||
if (Lumix::equalStrings(ext, "ogg")) return CLIP_HASH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int m_playing_clip;
|
||||
StudioApp& m_app;
|
||||
AssetBrowser& m_browser;
|
||||
};
|
||||
|
||||
|
||||
struct StudioAppPlugin : public StudioApp::IPlugin
|
||||
{
|
||||
explicit StudioAppPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
m_filter[0] = 0;
|
||||
m_is_opened = false;
|
||||
m_action = LUMIX_NEW(app.getWorldEditor()->getAllocator(), Action)("Clip manager", "clip_manager");
|
||||
m_action->func.bind<StudioAppPlugin, &StudioAppPlugin::onAction>(this);
|
||||
}
|
||||
|
||||
|
||||
void onAction() { m_is_opened = !m_is_opened; }
|
||||
|
||||
|
||||
void onWindowGUI() override
|
||||
{
|
||||
if (ImGui::BeginDock("Clip Manager", &m_is_opened))
|
||||
{
|
||||
ImGui::InputText("Filter", m_filter, Lumix::lengthOf(m_filter));
|
||||
|
||||
auto* audio_scene =
|
||||
static_cast<Lumix::AudioScene*>(m_app.getWorldEditor()->getScene(Lumix::crc32("audio")));
|
||||
int clip_count = audio_scene->getClipCount();
|
||||
for (int clip_id = 0; clip_id < clip_count; ++clip_id)
|
||||
{
|
||||
auto* clip_info = audio_scene->getClipInfo(clip_id);
|
||||
|
||||
if (m_filter[0] != 0 && Lumix::stristr(clip_info->name, m_filter) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode((const void*)(uintptr)clip_id, "%s", clip_info->name))
|
||||
{
|
||||
char buf[30];
|
||||
Lumix::copyString(buf, Lumix::lengthOf(buf), clip_info->name);
|
||||
if (ImGui::InputText("Name", buf, sizeof(buf)))
|
||||
{
|
||||
Lumix::copyString(clip_info->name, buf);
|
||||
clip_info->name_hash = Lumix::crc32(buf);
|
||||
}
|
||||
auto* clip = audio_scene->getClipInfo(clip_id)->clip;
|
||||
char path[Lumix::MAX_PATH_LENGTH];
|
||||
Lumix::copyString(path, clip ? clip->getPath().c_str() : "");
|
||||
if (m_app.getAssetBrowser()->resourceInput("Clip", "", path, Lumix::lengthOf(path), CLIP_HASH))
|
||||
{
|
||||
audio_scene->setClip(clip_id, Lumix::Path(path));
|
||||
}
|
||||
bool looped = audio_scene->getClipInfo(clip_id)->looped;
|
||||
if (ImGui::Checkbox("Looped", &looped))
|
||||
{
|
||||
clip_info->looped = looped;
|
||||
}
|
||||
if (ImGui::Button("Remove"))
|
||||
{
|
||||
audio_scene->removeClip(clip_info);
|
||||
--clip_count;
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Button("Add"))
|
||||
{
|
||||
audio_scene->addClip("test", Lumix::Path("test.ogg"));
|
||||
}
|
||||
}
|
||||
ImGui::EndDock();
|
||||
}
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
char m_filter[256];
|
||||
bool m_is_opened;
|
||||
};
|
||||
|
||||
|
||||
struct EditorPlugin : public WorldEditor::Plugin
|
||||
{
|
||||
explicit EditorPlugin(WorldEditor& editor)
|
||||
: m_editor(editor)
|
||||
{
|
||||
}
|
||||
|
||||
bool showGizmo(ComponentUID cmp) override
|
||||
{
|
||||
static const uint32 ECHO_ZONE_HASH = crc32("echo_zone");
|
||||
|
||||
if (cmp.type == ECHO_ZONE_HASH)
|
||||
{
|
||||
auto* audio_scene = static_cast<AudioScene*>(cmp.scene);
|
||||
float radius = audio_scene->getEchoZoneRadius(cmp.index);
|
||||
Universe& universe = audio_scene->getUniverse();
|
||||
Vec3 pos = universe.getPosition(cmp.entity);
|
||||
|
||||
auto* scene = static_cast<RenderScene*>(m_editor.getScene(crc32("renderer")));
|
||||
if (!scene) return true;
|
||||
scene->addDebugSphere(pos, radius, 0xff0000ff, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldEditor& m_editor;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(audio)
|
||||
{
|
||||
auto& editor = *app.getWorldEditor();
|
||||
auto& allocator = editor.getAllocator();
|
||||
|
||||
auto* asset_browser_plugin = LUMIX_NEW(allocator, AssetBrowserPlugin)(app);
|
||||
app.getAssetBrowser()->addPlugin(*asset_browser_plugin);
|
||||
|
||||
auto* app_plugin = LUMIX_NEW(allocator, StudioAppPlugin)(app);
|
||||
app.addPlugin(*app_plugin);
|
||||
|
||||
auto* plugin = LUMIX_NEW(allocator, EditorPlugin)(editor);
|
||||
editor.addPlugin(*plugin);
|
||||
}
|
573
src/lua_script/editor/plugins.cpp
Normal file
573
src/lua_script/editor/plugins.cpp
Normal file
|
@ -0,0 +1,573 @@
|
|||
#include "engine/array.h"
|
||||
#include "engine/base_proxy_allocator.h"
|
||||
#include "engine/binary_array.h"
|
||||
#include "engine/blob.h"
|
||||
#include "engine/crc32.h"
|
||||
#include "engine/fs/file_system.h"
|
||||
#include "engine/iallocator.h"
|
||||
#include "engine/json_serializer.h"
|
||||
#include "engine/log.h"
|
||||
#include "engine/lua_wrapper.h"
|
||||
#include "engine/path_utils.h"
|
||||
#include "engine/resource_manager.h"
|
||||
#include "engine/debug/debug.h"
|
||||
#include "editor/asset_browser.h"
|
||||
#include "editor/ieditor_command.h"
|
||||
#include "editor/property_grid.h"
|
||||
#include "editor/studio_app.h"
|
||||
#include "editor/utils.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/property_register.h"
|
||||
#include "engine/property_descriptor.h"
|
||||
#include "engine/iplugin.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "lua_script/lua_script_manager.h"
|
||||
#include "lua_script/lua_script_system.h"
|
||||
#include "engine/plugin_manager.h"
|
||||
#include "engine/universe/universe.h"
|
||||
|
||||
|
||||
using namespace Lumix;
|
||||
|
||||
|
||||
static const uint32 LUA_SCRIPT_HASH = crc32("lua_script");
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
int DragFloat(lua_State* L)
|
||||
{
|
||||
auto* name = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
float value = Lumix::LuaWrapper::checkArg<float>(L, 2);
|
||||
bool changed = ImGui::DragFloat(name, &value);
|
||||
lua_pushboolean(L, changed);
|
||||
lua_pushnumber(L, value);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
int Button(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool clicked = ImGui::Button(label);
|
||||
lua_pushboolean(L, clicked);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int Checkbox(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool b = Lumix::LuaWrapper::checkArg<bool>(L, 2);
|
||||
bool clicked = ImGui::Checkbox(label, &b);
|
||||
lua_pushboolean(L, clicked);
|
||||
lua_pushboolean(L, b);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
int Image(lua_State* L)
|
||||
{
|
||||
auto* texture_id = Lumix::LuaWrapper::checkArg<void*>(L, 1);
|
||||
float size_x = Lumix::LuaWrapper::checkArg<float>(L, 2);
|
||||
float size_y = Lumix::LuaWrapper::checkArg<float>(L, 3);
|
||||
ImGui::Image(texture_id, ImVec2(size_x, size_y));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Begin(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool res = ImGui::Begin(label);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int BeginDock(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool res = ImGui::BeginDock(label);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int SameLine(lua_State* L)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void registerCFunction(lua_State* L, const char* name, lua_CFunction f)
|
||||
{
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushcfunction(L, f);
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
|
||||
struct PropertyGridPlugin : public PropertyGrid::IPlugin
|
||||
{
|
||||
struct AddScriptCommand : public IEditorCommand
|
||||
{
|
||||
AddScriptCommand() {}
|
||||
|
||||
|
||||
explicit AddScriptCommand(WorldEditor& editor)
|
||||
{
|
||||
scene = static_cast<LuaScriptScene*>(editor.getScene(crc32("lua_script")));
|
||||
}
|
||||
|
||||
|
||||
bool execute() override
|
||||
{
|
||||
scr_index = scene->addScript(cmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void undo() override { scene->removeScript(cmp, scr_index); }
|
||||
|
||||
|
||||
void serialize(JsonSerializer& serializer) override { serializer.serialize("component", cmp); }
|
||||
|
||||
|
||||
void deserialize(JsonSerializer& serializer) override { serializer.deserialize("component", cmp, 0); }
|
||||
|
||||
|
||||
uint32 getType() override
|
||||
{
|
||||
static const uint32 hash = crc32("add_script");
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
bool merge(IEditorCommand& command) override { return false; }
|
||||
|
||||
|
||||
LuaScriptScene* scene;
|
||||
ComponentIndex cmp;
|
||||
int scr_index;
|
||||
};
|
||||
|
||||
|
||||
struct RemoveScriptCommand : public IEditorCommand
|
||||
{
|
||||
explicit RemoveScriptCommand(WorldEditor& editor)
|
||||
: blob(editor.getAllocator())
|
||||
{
|
||||
scene = static_cast<LuaScriptScene*>(editor.getScene(crc32("lua_script")));
|
||||
}
|
||||
|
||||
|
||||
explicit RemoveScriptCommand(IAllocator& allocator)
|
||||
: blob(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool execute() override
|
||||
{
|
||||
scene->serializeScript(cmp, scr_index, blob);
|
||||
scene->removeScript(cmp, scr_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void undo() override
|
||||
{
|
||||
scene->insertScript(cmp, scr_index);
|
||||
InputBlob input(blob);
|
||||
scene->deserializeScript(cmp, scr_index, input);
|
||||
}
|
||||
|
||||
|
||||
void serialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.serialize("component", cmp);
|
||||
serializer.serialize("scr_index", scr_index);
|
||||
}
|
||||
|
||||
|
||||
void deserialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.deserialize("component", cmp, 0);
|
||||
serializer.deserialize("scr_index", scr_index, 0);
|
||||
}
|
||||
|
||||
|
||||
uint32 getType() override
|
||||
{
|
||||
static const uint32 hash = crc32("remove_script");
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
bool merge(IEditorCommand& command) override { return false; }
|
||||
|
||||
OutputBlob blob;
|
||||
LuaScriptScene* scene;
|
||||
ComponentIndex cmp;
|
||||
int scr_index;
|
||||
};
|
||||
|
||||
|
||||
struct SetPropertyCommand : public IEditorCommand
|
||||
{
|
||||
explicit SetPropertyCommand(WorldEditor& editor)
|
||||
: property_name(editor.getAllocator())
|
||||
, value(editor.getAllocator())
|
||||
, old_value(editor.getAllocator())
|
||||
{
|
||||
scene = static_cast<LuaScriptScene*>(editor.getScene(crc32("lua_script")));
|
||||
}
|
||||
|
||||
|
||||
SetPropertyCommand(LuaScriptScene* scene,
|
||||
ComponentIndex cmp,
|
||||
int scr_index,
|
||||
const char* property_name,
|
||||
const char* val,
|
||||
IAllocator& allocator)
|
||||
: property_name(property_name, allocator)
|
||||
, value(val, allocator)
|
||||
, old_value(allocator)
|
||||
, component(cmp)
|
||||
, script_index(scr_index)
|
||||
{
|
||||
this->scene = scene;
|
||||
if (property_name[0] == '-')
|
||||
{
|
||||
old_value = scene->getScriptPath(component, script_index).c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
tmp[0] = '\0';
|
||||
uint32 prop_name_hash = crc32(property_name);
|
||||
scene->getPropertyValue(cmp, scr_index, property_name, tmp, lengthOf(tmp));
|
||||
old_value = tmp;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool execute() override
|
||||
{
|
||||
if (property_name.length() > 0 && property_name[0] == '-')
|
||||
{
|
||||
scene->setScriptPath(component, script_index, Path(value.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
scene->setPropertyValue(component, script_index, property_name.c_str(), value.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void undo() override
|
||||
{
|
||||
if (property_name.length() > 0 && property_name[0] == '-')
|
||||
{
|
||||
scene->setScriptPath(component, script_index, Path(old_value.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
scene->setPropertyValue(component, script_index, property_name.c_str(), old_value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void serialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.serialize("component", component);
|
||||
serializer.serialize("script_index", script_index);
|
||||
serializer.serialize("property_name", property_name.c_str());
|
||||
serializer.serialize("value", value.c_str());
|
||||
serializer.serialize("old_value", old_value.c_str());
|
||||
}
|
||||
|
||||
|
||||
void deserialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.deserialize("component", component, 0);
|
||||
serializer.deserialize("script_index", script_index, 0);
|
||||
char buf[256];
|
||||
serializer.deserialize("property_name", buf, lengthOf(buf), "");
|
||||
property_name = buf;
|
||||
serializer.deserialize("value", buf, lengthOf(buf), "");
|
||||
value = buf;
|
||||
serializer.deserialize("old_value", buf, lengthOf(buf), "");
|
||||
old_value = buf;
|
||||
}
|
||||
|
||||
|
||||
uint32 getType() override
|
||||
{
|
||||
static const uint32 hash = crc32("set_script_property");
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
bool merge(IEditorCommand& command) override
|
||||
{
|
||||
auto& cmd = static_cast<SetPropertyCommand&>(command);
|
||||
if (cmd.script_index == script_index && cmd.property_name == property_name)
|
||||
{
|
||||
cmd.value = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
LuaScriptScene* scene;
|
||||
string property_name;
|
||||
string value;
|
||||
string old_value;
|
||||
ComponentIndex component;
|
||||
int script_index;
|
||||
};
|
||||
|
||||
|
||||
explicit PropertyGridPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
lua_State* L = app.getWorldEditor()->getEngine().getState();
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, "ImGui");
|
||||
|
||||
registerCFunction(L, "DragFloat", &DragFloat);
|
||||
registerCFunction(L, "Button", &Button);
|
||||
registerCFunction(L, "Checkbox", &Checkbox);
|
||||
registerCFunction(L, "SameLine", &SameLine);
|
||||
registerCFunction(L, "BeginPopup", &LuaWrapper::wrap<decltype(&ImGui::BeginPopup), &ImGui::BeginPopup>);
|
||||
registerCFunction(L, "EndPopup", &LuaWrapper::wrap<decltype(&ImGui::EndPopup), &ImGui::EndPopup>);
|
||||
registerCFunction(L, "OpenPopup", &LuaWrapper::wrap<decltype(&ImGui::OpenPopup), &ImGui::OpenPopup>);
|
||||
registerCFunction(L, "BeginDock", &LuaWrapper::wrap<decltype(&BeginDock), &BeginDock>);
|
||||
registerCFunction(L, "EndDock", &LuaWrapper::wrap<decltype(&ImGui::EndDock), &ImGui::EndDock>);
|
||||
registerCFunction(L, "Begin", &LuaWrapper::wrap<decltype(&Begin), &Begin>);
|
||||
registerCFunction(L, "End", &LuaWrapper::wrap<decltype(&ImGui::End), &ImGui::End>);
|
||||
registerCFunction(L, "Image", &LuaWrapper::wrap<decltype(&Image), &Image>);
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
|
||||
void onGUI(PropertyGrid& grid, Lumix::ComponentUID cmp) override
|
||||
{
|
||||
if (cmp.type != LUA_SCRIPT_HASH) return;
|
||||
|
||||
auto* scene = static_cast<LuaScriptScene*>(cmp.scene);
|
||||
auto& editor = *m_app.getWorldEditor();
|
||||
auto& allocator = editor.getAllocator();
|
||||
|
||||
if (ImGui::Button("Add script"))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, AddScriptCommand);
|
||||
cmd->scene = scene;
|
||||
cmd->cmp = cmp.index;
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
|
||||
for (int j = 0; j < scene->getScriptCount(cmp.index); ++j)
|
||||
{
|
||||
char buf[MAX_PATH_LENGTH];
|
||||
copyString(buf, scene->getScriptPath(cmp.index, j).c_str());
|
||||
StaticString<Lumix::MAX_PATH_LENGTH + 20> header;
|
||||
PathUtils::getBasename(header.data, lengthOf(header.data), buf);
|
||||
if (header.data[0] == 0) header << j;
|
||||
header << "###" << j;
|
||||
if (ImGui::CollapsingHeader(header))
|
||||
{
|
||||
ImGui::PushID(j);
|
||||
if (ImGui::Button("Remove script"))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, RemoveScriptCommand)(allocator);
|
||||
cmd->cmp = cmp.index;
|
||||
cmd->scr_index = j;
|
||||
cmd->scene = scene;
|
||||
editor.executeCommand(cmd);
|
||||
ImGui::PopID();
|
||||
break;
|
||||
}
|
||||
if (m_app.getAssetBrowser()->resourceInput("Source", "src", buf, lengthOf(buf), LUA_SCRIPT_HASH))
|
||||
{
|
||||
auto* cmd =
|
||||
LUMIX_NEW(allocator, SetPropertyCommand)(scene, cmp.index, j, "-source", buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
for (int k = 0, kc = scene->getPropertyCount(cmp.index, j); k < kc; ++k)
|
||||
{
|
||||
char buf[256];
|
||||
const char* property_name = scene->getPropertyName(cmp.index, j, k);
|
||||
if (!property_name) continue;
|
||||
scene->getPropertyValue(cmp.index, j, property_name, buf, lengthOf(buf));
|
||||
switch (scene->getPropertyType(cmp.index, j, k))
|
||||
{
|
||||
case LuaScriptScene::Property::BOOLEAN:
|
||||
{
|
||||
bool b = equalStrings(buf, "true");
|
||||
if (ImGui::Checkbox(property_name, &b))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, b ? "true" : "false", allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LuaScriptScene::Property::FLOAT:
|
||||
{
|
||||
float f = (float)atof(buf);
|
||||
if (ImGui::DragFloat(property_name, &f))
|
||||
{
|
||||
Lumix::toCString(f, buf, sizeof(buf), 5);
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LuaScriptScene::Property::ENTITY:
|
||||
{
|
||||
Lumix::Entity e;
|
||||
Lumix::fromCString(buf, sizeof(buf), &e);
|
||||
if (grid.entityInput(property_name, StaticString<50>(property_name, cmp.index), e))
|
||||
{
|
||||
Lumix::toCString(e, buf, sizeof(buf));
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LuaScriptScene::Property::ANY:
|
||||
if (ImGui::InputText(property_name, buf, sizeof(buf)))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (auto* call = scene->beginFunctionCall(cmp.index, j, "onGUI"))
|
||||
{
|
||||
scene->endFunctionCall(*call);
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StudioApp& m_app;
|
||||
};
|
||||
|
||||
|
||||
struct AssetBrowserPlugin : AssetBrowser::IPlugin
|
||||
{
|
||||
explicit AssetBrowserPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
m_text_buffer[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
|
||||
{
|
||||
if (type != LUA_SCRIPT_HASH) return false;
|
||||
|
||||
auto* script = static_cast<Lumix::LuaScript*>(resource);
|
||||
|
||||
if (m_text_buffer[0] == '\0')
|
||||
{
|
||||
Lumix::copyString(m_text_buffer, script->getSourceCode());
|
||||
}
|
||||
ImGui::InputTextMultiline("Code", m_text_buffer, sizeof(m_text_buffer), ImVec2(0, 300));
|
||||
if (ImGui::Button("Save"))
|
||||
{
|
||||
auto& fs = m_app.getWorldEditor()->getEngine().getFileSystem();
|
||||
auto* file = fs.open(fs.getDefaultDevice(), resource->getPath(), Lumix::FS::Mode::CREATE_AND_WRITE);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
Lumix::g_log_warning.log("Lua Script") << "Could not save " << resource->getPath();
|
||||
return true;
|
||||
}
|
||||
|
||||
file->write(m_text_buffer, Lumix::stringLength(m_text_buffer));
|
||||
fs.close(*file);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Open in external editor"))
|
||||
{
|
||||
m_app.getAssetBrowser()->openInExternalEditor(resource);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Lumix::uint32 getResourceType(const char* ext) override
|
||||
{
|
||||
if (equalStrings(ext, "lua")) return LUA_SCRIPT_HASH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void onResourceUnloaded(Lumix::Resource*) override { m_text_buffer[0] = 0; }
|
||||
const char* getName() const override { return "Lua Script"; }
|
||||
|
||||
|
||||
bool hasResourceManager(Lumix::uint32 type) const override { return type == LUA_SCRIPT_HASH; }
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
char m_text_buffer[8192];
|
||||
bool m_is_opened;
|
||||
};
|
||||
|
||||
|
||||
} // anonoymous namespace
|
||||
|
||||
|
||||
IEditorCommand* createAddScriptCommand(WorldEditor& editor)
|
||||
{
|
||||
return LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin::AddScriptCommand)(editor);
|
||||
}
|
||||
|
||||
|
||||
IEditorCommand* createSetPropertyCommand(WorldEditor& editor)
|
||||
{
|
||||
return LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin::SetPropertyCommand)(editor);
|
||||
}
|
||||
|
||||
|
||||
IEditorCommand* createRemoveScriptCommand(WorldEditor& editor)
|
||||
{
|
||||
return LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin::RemoveScriptCommand)(editor);
|
||||
}
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(lua_script)
|
||||
{
|
||||
auto& editor = *app.getWorldEditor();
|
||||
editor.registerEditorCommandCreator("add_script", createAddScriptCommand);
|
||||
editor.registerEditorCommandCreator("remove_script", createRemoveScriptCommand);
|
||||
editor.registerEditorCommandCreator("set_script_property", createSetPropertyCommand);
|
||||
|
||||
auto* plugin = LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin)(app);
|
||||
app.getPropertyGrid()->addPlugin(*plugin);
|
||||
|
||||
auto* asset_browser_plugin = LUMIX_NEW(editor.getAllocator(), AssetBrowserPlugin)(app);
|
||||
app.getAssetBrowser()->addPlugin(*asset_browser_plugin);
|
||||
}
|
||||
|
||||
|
|
@ -12,17 +12,10 @@
|
|||
#include "engine/path_utils.h"
|
||||
#include "engine/resource_manager.h"
|
||||
#include "engine/debug/debug.h"
|
||||
#include "editor/asset_browser.h"
|
||||
#include "editor/ieditor_command.h"
|
||||
#include "editor/property_grid.h"
|
||||
#include "editor/studio_app.h"
|
||||
#include "editor/utils.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/property_register.h"
|
||||
#include "engine/property_descriptor.h"
|
||||
#include "engine/iplugin.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "lua_script/lua_script_manager.h"
|
||||
#include "engine/plugin_manager.h"
|
||||
#include "engine/universe/universe.h"
|
||||
|
@ -65,27 +58,6 @@ namespace Lumix
|
|||
|
||||
struct LuaScriptSceneImpl : public LuaScriptScene
|
||||
{
|
||||
struct Property
|
||||
{
|
||||
enum Type : int
|
||||
{
|
||||
BOOLEAN,
|
||||
FLOAT,
|
||||
ENTITY,
|
||||
ANY
|
||||
};
|
||||
|
||||
explicit Property(IAllocator& allocator)
|
||||
: stored_value(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 name_hash;
|
||||
Type type;
|
||||
string stored_value;
|
||||
};
|
||||
|
||||
|
||||
struct UpdateData
|
||||
{
|
||||
LuaScript* script;
|
||||
|
@ -359,6 +331,24 @@ namespace Lumix
|
|||
}
|
||||
|
||||
|
||||
int getPropertyCount(ComponentIndex cmp, int scr_index) override
|
||||
{
|
||||
return m_scripts[cmp]->m_scripts[scr_index].m_properties.size();
|
||||
}
|
||||
|
||||
|
||||
const char* getPropertyName(ComponentIndex cmp, int scr_index, int prop_index) override
|
||||
{
|
||||
return getPropertyName(m_scripts[cmp]->m_scripts[scr_index].m_properties[prop_index].name_hash);
|
||||
}
|
||||
|
||||
|
||||
Property::Type getPropertyType(ComponentIndex cmp, int scr_index, int prop_index) override
|
||||
{
|
||||
return m_scripts[cmp]->m_scripts[scr_index].m_properties[prop_index].type;
|
||||
}
|
||||
|
||||
|
||||
~LuaScriptSceneImpl()
|
||||
{
|
||||
unloadAllScripts();
|
||||
|
@ -684,7 +674,7 @@ namespace Lumix
|
|||
void setPropertyValue(Lumix::ComponentIndex cmp,
|
||||
int scr_index,
|
||||
const char* name,
|
||||
const char* value)
|
||||
const char* value) override
|
||||
{
|
||||
if (!m_scripts[cmp]) return;
|
||||
if(!m_scripts[cmp]->m_scripts[scr_index].m_state) return;
|
||||
|
@ -877,6 +867,24 @@ namespace Lumix
|
|||
}
|
||||
|
||||
|
||||
void getPropertyValue(ComponentIndex cmp, int scr_index, const char* property_name, char* out, int max_size) override
|
||||
{
|
||||
ASSERT(max_size > 0);
|
||||
|
||||
uint32 hash = crc32(property_name);
|
||||
auto& inst = m_scripts[cmp]->m_scripts[scr_index];
|
||||
for (auto& prop : inst.m_properties)
|
||||
{
|
||||
if (prop.name_hash == hash)
|
||||
{
|
||||
getProperty(prop, property_name, inst, out, max_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
|
||||
void getProperty(Property& prop, const char* prop_name, ScriptInstance& scr, char* out, int max_size)
|
||||
{
|
||||
if(max_size <= 0) return;
|
||||
|
@ -1134,27 +1142,27 @@ namespace Lumix
|
|||
}
|
||||
|
||||
|
||||
void insertScript(ComponentIndex cmp, int idx)
|
||||
void insertScript(ComponentIndex cmp, int idx) override
|
||||
{
|
||||
m_scripts[cmp]->m_scripts.emplaceAt(idx, m_system.m_allocator);
|
||||
}
|
||||
|
||||
|
||||
int addScript(ComponentIndex cmp)
|
||||
int addScript(ComponentIndex cmp) override
|
||||
{
|
||||
m_scripts[cmp]->m_scripts.emplace(m_system.m_allocator);
|
||||
return m_scripts[cmp]->m_scripts.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
void removeScript(ComponentIndex cmp, int scr_index)
|
||||
void removeScript(ComponentIndex cmp, int scr_index) override
|
||||
{
|
||||
setScriptPath(cmp, scr_index, Path());
|
||||
m_scripts[cmp]->m_scripts.eraseFast(scr_index);
|
||||
}
|
||||
|
||||
|
||||
void serializeScript(ComponentIndex cmp, int scr_index, OutputBlob& blob)
|
||||
void serializeScript(ComponentIndex cmp, int scr_index, OutputBlob& blob) override
|
||||
{
|
||||
auto& scr = m_scripts[cmp]->m_scripts[scr_index];
|
||||
blob.writeString(scr.m_script ? scr.m_script->getPath().c_str() : "");
|
||||
|
@ -1169,7 +1177,7 @@ namespace Lumix
|
|||
}
|
||||
|
||||
|
||||
void deserializeScript(ComponentIndex cmp, int scr_index, InputBlob& blob)
|
||||
void deserializeScript(ComponentIndex cmp, int scr_index, InputBlob& blob) override
|
||||
{
|
||||
auto& scr = m_scripts[cmp]->m_scripts[scr_index];
|
||||
int count;
|
||||
|
@ -1255,579 +1263,6 @@ namespace Lumix
|
|||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
int DragFloat(lua_State* L)
|
||||
{
|
||||
auto* name = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
float value = Lumix::LuaWrapper::checkArg<float>(L, 2);
|
||||
bool changed = ImGui::DragFloat(name, &value);
|
||||
lua_pushboolean(L, changed);
|
||||
lua_pushnumber(L, value);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
int Button(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool clicked = ImGui::Button(label);
|
||||
lua_pushboolean(L, clicked);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int Checkbox(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool b = Lumix::LuaWrapper::checkArg<bool>(L, 2);
|
||||
bool clicked = ImGui::Checkbox(label, &b);
|
||||
lua_pushboolean(L, clicked);
|
||||
lua_pushboolean(L, b);
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
int Image(lua_State* L)
|
||||
{
|
||||
auto* texture_id = Lumix::LuaWrapper::checkArg<void*>(L, 1);
|
||||
float size_x = Lumix::LuaWrapper::checkArg<float>(L, 2);
|
||||
float size_y = Lumix::LuaWrapper::checkArg<float>(L, 3);
|
||||
ImGui::Image(texture_id, ImVec2(size_x, size_y));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int Begin(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool res = ImGui::Begin(label);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int BeginDock(lua_State* L)
|
||||
{
|
||||
auto* label = Lumix::LuaWrapper::checkArg<const char*>(L, 1);
|
||||
bool res = ImGui::BeginDock(label);
|
||||
lua_pushboolean(L, res);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int SameLine(lua_State* L)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void registerCFunction(lua_State* L, const char* name, lua_CFunction f)
|
||||
{
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushcfunction(L, f);
|
||||
lua_setfield(L, -2, name);
|
||||
}
|
||||
|
||||
|
||||
struct PropertyGridPlugin : public PropertyGrid::IPlugin
|
||||
{
|
||||
struct AddScriptCommand : public IEditorCommand
|
||||
{
|
||||
AddScriptCommand(){}
|
||||
|
||||
|
||||
explicit AddScriptCommand(WorldEditor& editor)
|
||||
{
|
||||
scene = static_cast<LuaScriptSceneImpl*>(editor.getScene(crc32("lua_script")));
|
||||
}
|
||||
|
||||
|
||||
bool execute() override
|
||||
{
|
||||
scr_index = scene->addScript(cmp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void undo() override
|
||||
{
|
||||
scene->removeScript(cmp, scr_index);
|
||||
}
|
||||
|
||||
|
||||
void serialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.serialize("component", cmp);
|
||||
}
|
||||
|
||||
|
||||
void deserialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.deserialize("component", cmp, 0);
|
||||
}
|
||||
|
||||
|
||||
uint32 getType() override
|
||||
{
|
||||
static const uint32 hash = crc32("add_script");
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
bool merge(IEditorCommand& command) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
LuaScriptSceneImpl* scene;
|
||||
ComponentIndex cmp;
|
||||
int scr_index;
|
||||
};
|
||||
|
||||
|
||||
struct RemoveScriptCommand : public IEditorCommand
|
||||
{
|
||||
explicit RemoveScriptCommand(WorldEditor& editor)
|
||||
: blob(editor.getAllocator())
|
||||
{
|
||||
scene = static_cast<LuaScriptSceneImpl*>(editor.getScene(crc32("lua_script")));
|
||||
}
|
||||
|
||||
|
||||
explicit RemoveScriptCommand(IAllocator& allocator)
|
||||
: blob(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool execute() override
|
||||
{
|
||||
scene->serializeScript(cmp, scr_index, blob);
|
||||
scene->removeScript(cmp, scr_index);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void undo() override
|
||||
{
|
||||
scene->insertScript(cmp, scr_index);
|
||||
InputBlob input(blob);
|
||||
scene->deserializeScript(cmp, scr_index, input);
|
||||
}
|
||||
|
||||
|
||||
void serialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.serialize("component", cmp);
|
||||
serializer.serialize("scr_index", scr_index);
|
||||
}
|
||||
|
||||
|
||||
void deserialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.deserialize("component", cmp, 0);
|
||||
serializer.deserialize("scr_index", scr_index, 0);
|
||||
}
|
||||
|
||||
|
||||
uint32 getType() override
|
||||
{
|
||||
static const uint32 hash = crc32("remove_script");
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
bool merge(IEditorCommand& command) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OutputBlob blob;
|
||||
LuaScriptSceneImpl* scene;
|
||||
ComponentIndex cmp;
|
||||
int scr_index;
|
||||
};
|
||||
|
||||
|
||||
struct SetPropertyCommand : public IEditorCommand
|
||||
{
|
||||
explicit SetPropertyCommand(WorldEditor& editor)
|
||||
: property_name(editor.getAllocator())
|
||||
, value(editor.getAllocator())
|
||||
, old_value(editor.getAllocator())
|
||||
{
|
||||
scene = static_cast<LuaScriptSceneImpl*>(editor.getScene(crc32("lua_script")));
|
||||
}
|
||||
|
||||
|
||||
SetPropertyCommand(LuaScriptSceneImpl* scene,
|
||||
ComponentIndex cmp,
|
||||
int scr_index,
|
||||
const char* property_name,
|
||||
const char* val,
|
||||
IAllocator& allocator)
|
||||
: property_name(property_name, allocator)
|
||||
, value(val, allocator)
|
||||
, old_value(allocator)
|
||||
, component(cmp)
|
||||
, script_index(scr_index)
|
||||
{
|
||||
this->scene = scene;
|
||||
if (property_name[0] == '-')
|
||||
{
|
||||
old_value = scene->getScriptPath(component, script_index).c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
tmp[0] = '\0';
|
||||
auto& inst = scene->m_scripts[cmp]->m_scripts[scr_index];
|
||||
uint32 prop_name_hash = crc32(property_name);
|
||||
for (auto& prop : inst.m_properties)
|
||||
{
|
||||
if (prop.name_hash == prop_name_hash)
|
||||
{
|
||||
scene->getProperty(prop, property_name, inst, tmp, lengthOf(tmp));
|
||||
break;
|
||||
}
|
||||
}
|
||||
old_value = tmp;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool execute() override
|
||||
{
|
||||
if (property_name.length() > 0 && property_name[0] == '-')
|
||||
{
|
||||
scene->setScriptPath(component, script_index, Path(value.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
scene->setPropertyValue(component, script_index, property_name.c_str(), value.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void undo() override
|
||||
{
|
||||
if (property_name.length() > 0 && property_name[0] == '-')
|
||||
{
|
||||
scene->setScriptPath(component, script_index, Path(old_value.c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
scene->setPropertyValue(component, script_index, property_name.c_str(), old_value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void serialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.serialize("component", component);
|
||||
serializer.serialize("script_index", script_index);
|
||||
serializer.serialize("property_name", property_name.c_str());
|
||||
serializer.serialize("value", value.c_str());
|
||||
serializer.serialize("old_value", old_value.c_str());
|
||||
}
|
||||
|
||||
|
||||
void deserialize(JsonSerializer& serializer) override
|
||||
{
|
||||
serializer.deserialize("component", component, 0);
|
||||
serializer.deserialize("script_index", script_index, 0);
|
||||
char buf[256];
|
||||
serializer.deserialize("property_name", buf, lengthOf(buf), "");
|
||||
property_name = buf;
|
||||
serializer.deserialize("value", buf, lengthOf(buf), "");
|
||||
value = buf;
|
||||
serializer.deserialize("old_value", buf, lengthOf(buf), "");
|
||||
old_value = buf;
|
||||
}
|
||||
|
||||
|
||||
uint32 getType() override
|
||||
{
|
||||
static const uint32 hash = crc32("set_script_property");
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
bool merge(IEditorCommand& command) override
|
||||
{
|
||||
auto& cmd = static_cast<SetPropertyCommand&>(command);
|
||||
if (cmd.script_index == script_index && cmd.property_name == property_name)
|
||||
{
|
||||
cmd.value = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
LuaScriptSceneImpl* scene;
|
||||
string property_name;
|
||||
string value;
|
||||
string old_value;
|
||||
ComponentIndex component;
|
||||
int script_index;
|
||||
};
|
||||
|
||||
|
||||
explicit PropertyGridPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
lua_State* L = app.getWorldEditor()->getEngine().getState();
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, "ImGui");
|
||||
|
||||
registerCFunction(L, "DragFloat", &DragFloat);
|
||||
registerCFunction(L, "Button", &Button);
|
||||
registerCFunction(L, "Checkbox", &Checkbox);
|
||||
registerCFunction(L, "SameLine", &SameLine);
|
||||
registerCFunction(L, "BeginPopup", &LuaWrapper::wrap<decltype(&ImGui::BeginPopup), &ImGui::BeginPopup>);
|
||||
registerCFunction(L, "EndPopup", &LuaWrapper::wrap<decltype(&ImGui::EndPopup), &ImGui::EndPopup>);
|
||||
registerCFunction(L, "OpenPopup", &LuaWrapper::wrap<decltype(&ImGui::OpenPopup), &ImGui::OpenPopup>);
|
||||
registerCFunction(L, "BeginDock", &LuaWrapper::wrap<decltype(&BeginDock), &BeginDock>);
|
||||
registerCFunction(L, "EndDock", &LuaWrapper::wrap<decltype(&ImGui::EndDock), &ImGui::EndDock>);
|
||||
registerCFunction(L, "Begin", &LuaWrapper::wrap<decltype(&Begin), &Begin>);
|
||||
registerCFunction(L, "End", &LuaWrapper::wrap<decltype(&ImGui::End), &ImGui::End>);
|
||||
registerCFunction(L, "Image", &LuaWrapper::wrap<decltype(&Image), &Image>);
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void onGUI(PropertyGrid& grid, Lumix::ComponentUID cmp) override
|
||||
{
|
||||
if (cmp.type != LUA_SCRIPT_HASH) return;
|
||||
|
||||
auto* scene = static_cast<LuaScriptSceneImpl*>(cmp.scene);
|
||||
auto& editor = *m_app.getWorldEditor();
|
||||
auto& allocator = editor.getAllocator();
|
||||
|
||||
if (ImGui::Button("Add script"))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, AddScriptCommand);
|
||||
cmd->scene = scene;
|
||||
cmd->cmp = cmp.index;
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
|
||||
for (int j = 0; j < scene->getScriptCount(cmp.index); ++j)
|
||||
{
|
||||
char buf[MAX_PATH_LENGTH];
|
||||
copyString(buf, scene->getScriptPath(cmp.index, j).c_str());
|
||||
StaticString<Lumix::MAX_PATH_LENGTH + 20> header;
|
||||
PathUtils::getBasename(header.data, lengthOf(header.data), buf);
|
||||
if (header.data[0] == 0) header << j;
|
||||
header << "###" << j;
|
||||
if (ImGui::CollapsingHeader(header))
|
||||
{
|
||||
ImGui::PushID(j);
|
||||
if (ImGui::Button("Remove script"))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, RemoveScriptCommand)(allocator);
|
||||
cmd->cmp = cmp.index;
|
||||
cmd->scr_index = j;
|
||||
cmd->scene = scene;
|
||||
editor.executeCommand(cmd);
|
||||
ImGui::PopID();
|
||||
break;
|
||||
}
|
||||
if (m_app.getAssetBrowser()->resourceInput("Source", "src", buf, lengthOf(buf), LUA_SCRIPT_HASH))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(scene, cmp.index, j, "-source", buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
auto& inst = scene->m_scripts[cmp.index]->m_scripts[j];
|
||||
if (inst.m_state)
|
||||
{
|
||||
for (auto& prop : inst.m_properties)
|
||||
{
|
||||
char buf[256];
|
||||
const char* property_name = scene->getPropertyName(prop.name_hash);
|
||||
if (!property_name) continue;
|
||||
scene->getProperty(prop, property_name, inst, buf, lengthOf(buf));
|
||||
switch (prop.type)
|
||||
{
|
||||
case LuaScriptSceneImpl::Property::BOOLEAN:
|
||||
{
|
||||
bool b = equalStrings(buf, "true");
|
||||
if (ImGui::Checkbox(property_name, &b))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, b ? "true" : "false", allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LuaScriptSceneImpl::Property::FLOAT:
|
||||
{
|
||||
float f = (float)atof(buf);
|
||||
if (ImGui::DragFloat(property_name, &f))
|
||||
{
|
||||
Lumix::toCString(f, buf, sizeof(buf), 5);
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LuaScriptSceneImpl::Property::ENTITY:
|
||||
{
|
||||
Lumix::Entity e;
|
||||
Lumix::fromCString(buf, sizeof(buf), &e);
|
||||
if (grid.entityInput(property_name,
|
||||
StaticString<50>(property_name, cmp.index),
|
||||
e))
|
||||
{
|
||||
Lumix::toCString(e, buf, sizeof(buf));
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LuaScriptSceneImpl::Property::ANY:
|
||||
if (ImGui::InputText(property_name, buf, sizeof(buf)))
|
||||
{
|
||||
auto* cmd = LUMIX_NEW(allocator, SetPropertyCommand)(
|
||||
scene, cmp.index, j, property_name, buf, allocator);
|
||||
editor.executeCommand(cmd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (auto* call = scene->beginFunctionCall(cmp.index, j, "onGUI"))
|
||||
{
|
||||
scene->endFunctionCall(*call);
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StudioApp& m_app;
|
||||
};
|
||||
|
||||
|
||||
struct AssetBrowserPlugin : AssetBrowser::IPlugin
|
||||
{
|
||||
explicit AssetBrowserPlugin(StudioApp& app)
|
||||
: m_app(app)
|
||||
{
|
||||
m_text_buffer[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
|
||||
{
|
||||
if (type != LUA_SCRIPT_HASH) return false;
|
||||
|
||||
auto* script = static_cast<Lumix::LuaScript*>(resource);
|
||||
|
||||
if (m_text_buffer[0] == '\0')
|
||||
{
|
||||
Lumix::copyString(m_text_buffer, script->getSourceCode());
|
||||
}
|
||||
ImGui::InputTextMultiline("Code", m_text_buffer, sizeof(m_text_buffer), ImVec2(0, 300));
|
||||
if (ImGui::Button("Save"))
|
||||
{
|
||||
auto& fs = m_app.getWorldEditor()->getEngine().getFileSystem();
|
||||
auto* file = fs.open(fs.getDefaultDevice(),
|
||||
resource->getPath(),
|
||||
Lumix::FS::Mode::CREATE_AND_WRITE);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
Lumix::g_log_warning.log("Lua Script") << "Could not save "
|
||||
<< resource->getPath();
|
||||
return true;
|
||||
}
|
||||
|
||||
file->write(m_text_buffer, Lumix::stringLength(m_text_buffer));
|
||||
fs.close(*file);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Open in external editor"))
|
||||
{
|
||||
m_app.getAssetBrowser()->openInExternalEditor(resource);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Lumix::uint32 getResourceType(const char* ext) override
|
||||
{
|
||||
if (equalStrings(ext, "lua")) return LUA_SCRIPT_HASH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void onResourceUnloaded(Lumix::Resource*) override { m_text_buffer[0] = 0; }
|
||||
const char* getName() const override { return "Lua Script"; }
|
||||
|
||||
|
||||
bool hasResourceManager(Lumix::uint32 type) const override
|
||||
{
|
||||
return type == LUA_SCRIPT_HASH;
|
||||
}
|
||||
|
||||
|
||||
StudioApp& m_app;
|
||||
char m_text_buffer[8192];
|
||||
bool m_is_opened;
|
||||
};
|
||||
|
||||
|
||||
} // anonoymous namespace
|
||||
|
||||
|
||||
IEditorCommand* createAddScriptCommand(WorldEditor& editor)
|
||||
{
|
||||
return LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin::AddScriptCommand)(editor);
|
||||
}
|
||||
|
||||
|
||||
IEditorCommand* createSetPropertyCommand(WorldEditor& editor)
|
||||
{
|
||||
return LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin::SetPropertyCommand)(editor);
|
||||
}
|
||||
|
||||
|
||||
IEditorCommand* createRemoveScriptCommand(WorldEditor& editor)
|
||||
{
|
||||
return LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin::RemoveScriptCommand)(editor);
|
||||
}
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(lua_script)
|
||||
{
|
||||
auto& editor = *app.getWorldEditor();
|
||||
editor.registerEditorCommandCreator("add_script", createAddScriptCommand);
|
||||
editor.registerEditorCommandCreator("remove_script", createRemoveScriptCommand);
|
||||
editor.registerEditorCommandCreator("set_script_property", createSetPropertyCommand);
|
||||
|
||||
auto* plugin = LUMIX_NEW(editor.getAllocator(), PropertyGridPlugin)(app);
|
||||
app.getPropertyGrid()->addPlugin(*plugin);
|
||||
|
||||
auto* asset_browser_plugin = LUMIX_NEW(editor.getAllocator(), AssetBrowserPlugin)(app);
|
||||
app.getAssetBrowser()->addPlugin(*asset_browser_plugin);
|
||||
}
|
||||
|
||||
|
||||
LUMIX_PLUGIN_ENTRY(lua_script)
|
||||
{
|
||||
return LUMIX_NEW(engine.getAllocator(), LuaScriptSystemImpl)(engine);
|
||||
|
|
|
@ -19,6 +19,27 @@ class LuaScript;
|
|||
class LuaScriptScene : public IScene
|
||||
{
|
||||
public:
|
||||
struct Property
|
||||
{
|
||||
enum Type : int
|
||||
{
|
||||
BOOLEAN,
|
||||
FLOAT,
|
||||
ENTITY,
|
||||
ANY
|
||||
};
|
||||
|
||||
explicit Property(IAllocator& allocator)
|
||||
: stored_value(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
uint32 name_hash;
|
||||
Type type;
|
||||
string stored_value;
|
||||
};
|
||||
|
||||
|
||||
class IFunctionCall
|
||||
{
|
||||
public:
|
||||
|
@ -40,6 +61,16 @@ public:
|
|||
virtual void endFunctionCall(IFunctionCall& caller) = 0;
|
||||
virtual int getScriptCount(ComponentIndex cmp) = 0;
|
||||
virtual lua_State* getState(ComponentIndex cmp, int scr_index) = 0;
|
||||
virtual void insertScript(ComponentIndex cmp, int idx) = 0;
|
||||
virtual int addScript(ComponentIndex cmp) = 0;
|
||||
virtual void removeScript(ComponentIndex cmp, int scr_index) = 0;
|
||||
virtual void serializeScript(ComponentIndex cmp, int scr_index, OutputBlob& blob) = 0;
|
||||
virtual void deserializeScript(ComponentIndex cmp, int scr_index, InputBlob& blob) = 0;
|
||||
virtual void setPropertyValue(Lumix::ComponentIndex cmp, int scr_index, const char* name, const char* value) = 0;
|
||||
virtual void getPropertyValue(ComponentIndex cmp, int scr_index, const char* property_name, char* out, int max_size) = 0;
|
||||
virtual int getPropertyCount(ComponentIndex cmp, int scr_index) = 0;
|
||||
virtual const char* getPropertyName(ComponentIndex cmp, int scr_index, int prop_index) = 0;
|
||||
virtual Property::Type getPropertyType(ComponentIndex cmp, int scr_index, int prop_index) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
186
src/physics/editor/plugins.cpp
Normal file
186
src/physics/editor/plugins.cpp
Normal file
|
@ -0,0 +1,186 @@
|
|||
#include "physics/physics_system.h"
|
||||
|
||||
#include <PxPhysicsAPI.h>
|
||||
|
||||
#include "engine/crc32.h"
|
||||
#include "engine/math_utils.h"
|
||||
#include "editor/studio_app.h"
|
||||
#include "editor/utils.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/universe/universe.h"
|
||||
#include "physics/physics_scene.h"
|
||||
#include "renderer/render_scene.h"
|
||||
|
||||
|
||||
using namespace Lumix;
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
static const uint32 BOX_ACTOR_HASH = crc32("box_rigid_actor");
|
||||
static const uint32 CONTROLLER_HASH = crc32("physical_controller");
|
||||
|
||||
|
||||
struct EditorPlugin : public WorldEditor::Plugin
|
||||
{
|
||||
explicit EditorPlugin(WorldEditor& editor)
|
||||
: m_editor(editor)
|
||||
{
|
||||
}
|
||||
|
||||
bool showGizmo(ComponentUID cmp) override
|
||||
{
|
||||
PhysicsScene* phy_scene = static_cast<PhysicsScene*>(cmp.scene);
|
||||
if (cmp.type == CONTROLLER_HASH)
|
||||
{
|
||||
auto* scene = static_cast<RenderScene*>(m_editor.getScene(crc32("renderer")));
|
||||
float height = phy_scene->getControllerHeight(cmp.index);
|
||||
float radius = phy_scene->getControllerRadius(cmp.index);
|
||||
|
||||
Universe& universe = scene->getUniverse();
|
||||
Vec3 pos = universe.getPosition(cmp.entity);
|
||||
scene->addDebugCapsule(pos, height, radius, 0xff0000ff, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cmp.type == BOX_ACTOR_HASH)
|
||||
{
|
||||
auto* scene = static_cast<RenderScene*>(m_editor.getScene(crc32("renderer")));
|
||||
Vec3 extents = phy_scene->getHalfExtents(cmp.index);
|
||||
|
||||
Universe& universe = scene->getUniverse();
|
||||
Matrix mtx = universe.getPositionAndRotation(cmp.entity);
|
||||
|
||||
scene->addDebugCube(mtx.getTranslation(),
|
||||
mtx.getXVector() * extents.x,
|
||||
mtx.getYVector() * extents.y,
|
||||
mtx.getZVector() * extents.z,
|
||||
0xffff0000,
|
||||
0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldEditor& m_editor;
|
||||
};
|
||||
|
||||
|
||||
struct StudioAppPlugin : public StudioApp::IPlugin
|
||||
{
|
||||
explicit StudioAppPlugin(Lumix::WorldEditor& editor)
|
||||
: m_editor(editor)
|
||||
{
|
||||
m_action = LUMIX_NEW(m_editor.getAllocator(), Action)("Physics", "physics");
|
||||
m_action->func.bind<StudioAppPlugin, &StudioAppPlugin::onAction>(this);
|
||||
m_is_window_opened = false;
|
||||
}
|
||||
|
||||
|
||||
void onAction() { m_is_window_opened = !m_is_window_opened; }
|
||||
|
||||
|
||||
void onWindowGUI() override
|
||||
{
|
||||
auto* scene = static_cast<PhysicsScene*>(m_editor.getScene(crc32("physics")));
|
||||
if (ImGui::BeginDock("Physics", &m_is_window_opened))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Layers"))
|
||||
{
|
||||
for (int i = 0; i < scene->getCollisionsLayersCount(); ++i)
|
||||
{
|
||||
char buf[30];
|
||||
copyString(buf, scene->getCollisionLayerName(i));
|
||||
char label[10];
|
||||
toCString(i, label, lengthOf(label));
|
||||
if (ImGui::InputText(label, buf, lengthOf(buf)))
|
||||
{
|
||||
scene->setCollisionLayerName(i, buf);
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Add layer"))
|
||||
{
|
||||
scene->addCollisionLayer();
|
||||
}
|
||||
if (scene->getCollisionsLayersCount() > 1)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Remove layer"))
|
||||
{
|
||||
scene->removeCollisionLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Collision matrix", nullptr, true, true))
|
||||
{
|
||||
ImGui::Columns(1 + scene->getCollisionsLayersCount());
|
||||
ImGui::NextColumn();
|
||||
ImGui::PushTextWrapPos(1);
|
||||
float basic_offset = 0;
|
||||
for (int i = 0, c = scene->getCollisionsLayersCount(); i < c; ++i)
|
||||
{
|
||||
auto* layer_name = scene->getCollisionLayerName(i);
|
||||
basic_offset = Math::maximum(basic_offset, ImGui::CalcTextSize(layer_name).x);
|
||||
}
|
||||
basic_offset += ImGui::GetStyle().FramePadding.x * 2 + ImGui::GetStyle().WindowPadding.x;
|
||||
|
||||
for (int i = 0, c = scene->getCollisionsLayersCount(); i < c; ++i)
|
||||
{
|
||||
auto* layer_name = scene->getCollisionLayerName(i);
|
||||
float offset = basic_offset + i * 35.0f;
|
||||
ImGui::SetColumnOffset(-1, offset);
|
||||
ImGui::Text(layer_name);
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
ImGui::Separator();
|
||||
for (int i = 0, c = scene->getCollisionsLayersCount(); i < c; ++i)
|
||||
{
|
||||
ImGui::Text(scene->getCollisionLayerName(i));
|
||||
ImGui::NextColumn();
|
||||
|
||||
for (int j = 0; j <= i; ++j)
|
||||
{
|
||||
bool b = scene->canLayersCollide(i, j);
|
||||
if (ImGui::Checkbox(StaticString<10>("###", i, "-") << j, &b))
|
||||
{
|
||||
scene->setLayersCanCollide(i, j, b);
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
for (int j = i + 1; j < c; ++j)
|
||||
{
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
}
|
||||
ImGui::Columns();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndDock();
|
||||
}
|
||||
|
||||
|
||||
bool m_is_window_opened;
|
||||
Lumix::WorldEditor& m_editor;
|
||||
};
|
||||
|
||||
|
||||
} // anonymous
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(physics)
|
||||
{
|
||||
StudioAppPlugin* plugin = LUMIX_NEW(app.getWorldEditor()->getAllocator(), StudioAppPlugin)(*app.getWorldEditor());
|
||||
app.addPlugin(*plugin);
|
||||
|
||||
auto& editor = *app.getWorldEditor();
|
||||
auto* editor_plugin = LUMIX_NEW(editor.getAllocator(), EditorPlugin)(editor);
|
||||
editor.addPlugin(*editor_plugin);
|
||||
}
|
||||
|
|
@ -7,9 +7,6 @@
|
|||
#include "engine/crc32.h"
|
||||
#include "engine/log.h"
|
||||
#include "engine/resource_manager.h"
|
||||
#include "editor/studio_app.h"
|
||||
#include "editor/utils.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/property_descriptor.h"
|
||||
#include "engine/property_register.h"
|
||||
|
@ -242,55 +239,7 @@ namespace Lumix
|
|||
return LUMIX_NEW(engine.getAllocator(), PhysicsSystemImpl)(engine);
|
||||
}
|
||||
|
||||
|
||||
struct EditorPlugin : public WorldEditor::Plugin
|
||||
{
|
||||
explicit EditorPlugin(WorldEditor& editor)
|
||||
: m_editor(editor)
|
||||
{
|
||||
}
|
||||
|
||||
bool showGizmo(ComponentUID cmp) override
|
||||
{
|
||||
PhysicsScene* phy_scene = static_cast<PhysicsScene*>(cmp.scene);
|
||||
if (cmp.type == CONTROLLER_HASH)
|
||||
{
|
||||
auto* scene = static_cast<RenderScene*>(m_editor.getScene(crc32("renderer")));
|
||||
float height = phy_scene->getControllerHeight(cmp.index);
|
||||
float radius = phy_scene->getControllerRadius(cmp.index);
|
||||
|
||||
Universe& universe = scene->getUniverse();
|
||||
Vec3 pos = universe.getPosition(cmp.entity);
|
||||
scene->addDebugCapsule(pos, height, radius, 0xff0000ff, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cmp.type == BOX_ACTOR_HASH)
|
||||
{
|
||||
auto* scene = static_cast<RenderScene*>(m_editor.getScene(crc32("renderer")));
|
||||
Vec3 extents = phy_scene->getHalfExtents(cmp.index);
|
||||
|
||||
Universe& universe = scene->getUniverse();
|
||||
Matrix mtx = universe.getPositionAndRotation(cmp.entity);
|
||||
|
||||
scene->addDebugCube(mtx.getTranslation(),
|
||||
mtx.getXVector() * extents.x,
|
||||
mtx.getYVector() * extents.y,
|
||||
mtx.getZVector() * extents.z,
|
||||
0xffff0000,
|
||||
0);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
WorldEditor& m_editor;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct CustomErrorCallback : public physx::PxErrorCallback
|
||||
{
|
||||
virtual void reportError(physx::PxErrorCode::Enum code, const char* message, const char* file, int line);
|
||||
|
@ -382,128 +331,6 @@ namespace Lumix
|
|||
g_log_error.log("Physics") << message;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
struct StudioAppPlugin : public StudioApp::IPlugin
|
||||
{
|
||||
explicit StudioAppPlugin(Lumix::WorldEditor& editor)
|
||||
: m_editor(editor)
|
||||
{
|
||||
m_action = LUMIX_NEW(m_editor.getAllocator(), Action)("Physics", "physics");
|
||||
m_action->func.bind<StudioAppPlugin, &StudioAppPlugin::onAction>(this);
|
||||
m_is_window_opened = false;
|
||||
}
|
||||
|
||||
|
||||
void onAction()
|
||||
{
|
||||
m_is_window_opened = !m_is_window_opened;
|
||||
}
|
||||
|
||||
|
||||
void onWindowGUI() override
|
||||
{
|
||||
auto* scene = static_cast<PhysicsScene*>(m_editor.getScene(crc32("physics")));
|
||||
if (ImGui::BeginDock("Physics", &m_is_window_opened))
|
||||
{
|
||||
if (ImGui::CollapsingHeader("Layers"))
|
||||
{
|
||||
for (int i = 0; i < scene->getCollisionsLayersCount(); ++i)
|
||||
{
|
||||
char buf[30];
|
||||
copyString(buf, scene->getCollisionLayerName(i));
|
||||
char label[10];
|
||||
toCString(i, label, lengthOf(label));
|
||||
if (ImGui::InputText(label, buf, lengthOf(buf)))
|
||||
{
|
||||
scene->setCollisionLayerName(i, buf);
|
||||
}
|
||||
}
|
||||
if (ImGui::Button("Add layer"))
|
||||
{
|
||||
scene->addCollisionLayer();
|
||||
}
|
||||
if (scene->getCollisionsLayersCount() > 1)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Remove layer"))
|
||||
{
|
||||
scene->removeCollisionLayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Collision matrix", nullptr, true, true))
|
||||
{
|
||||
ImGui::Columns(1 + scene->getCollisionsLayersCount());
|
||||
ImGui::NextColumn();
|
||||
ImGui::PushTextWrapPos(1);
|
||||
float basic_offset = 0;
|
||||
for (int i = 0, c = scene->getCollisionsLayersCount(); i < c; ++i)
|
||||
{
|
||||
auto* layer_name = scene->getCollisionLayerName(i);
|
||||
basic_offset = Math::maximum(basic_offset, ImGui::CalcTextSize(layer_name).x);
|
||||
}
|
||||
basic_offset += ImGui::GetStyle().FramePadding.x * 2 + ImGui::GetStyle().WindowPadding.x;
|
||||
|
||||
for (int i = 0, c = scene->getCollisionsLayersCount(); i < c; ++i)
|
||||
{
|
||||
auto* layer_name = scene->getCollisionLayerName(i);
|
||||
float offset = basic_offset + i * 35.0f;
|
||||
ImGui::SetColumnOffset(-1, offset);
|
||||
ImGui::Text(layer_name);
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
ImGui::Separator();
|
||||
for (int i = 0, c = scene->getCollisionsLayersCount(); i < c; ++i)
|
||||
{
|
||||
ImGui::Text(scene->getCollisionLayerName(i));
|
||||
ImGui::NextColumn();
|
||||
|
||||
for (int j = 0; j <= i; ++j)
|
||||
{
|
||||
bool b = scene->canLayersCollide(i, j);
|
||||
if (ImGui::Checkbox(StaticString<10>("###", i, "-") << j, &b))
|
||||
{
|
||||
scene->setLayersCanCollide(i, j, b);
|
||||
}
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
for (int j = i + 1; j < c; ++j)
|
||||
{
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
}
|
||||
ImGui::Columns();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndDock();
|
||||
}
|
||||
|
||||
|
||||
bool m_is_window_opened;
|
||||
Lumix::WorldEditor& m_editor;
|
||||
};
|
||||
|
||||
|
||||
} // anonymous
|
||||
|
||||
|
||||
LUMIX_STUDIO_ENTRY(physics)
|
||||
{
|
||||
StudioAppPlugin* plugin =
|
||||
LUMIX_NEW(app.getWorldEditor()->getAllocator(), StudioAppPlugin)(*app.getWorldEditor());
|
||||
app.addPlugin(*plugin);
|
||||
|
||||
auto& editor = *app.getWorldEditor();
|
||||
auto* editor_plugin = LUMIX_NEW(editor.getAllocator(), EditorPlugin)(editor);
|
||||
editor.addPlugin(*editor_plugin);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Lumix
|
||||
|
||||
|
|
|
@ -1,428 +0,0 @@
|
|||
#include "script_system.h"
|
||||
#include <Windows.h>
|
||||
#include "engine/core/iallocator.h"
|
||||
#include "engine/core/crc32.h"
|
||||
#include "engine/core/fs/file_system.h"
|
||||
#include "engine/core/fs/ifile.h"
|
||||
#include "engine/core/json_serializer.h"
|
||||
#include "engine/core/library.h"
|
||||
#include "engine/core/log.h"
|
||||
#include "engine/core/array.h"
|
||||
#include "editor/world_editor.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/universe/universe.h"
|
||||
|
||||
|
||||
static const uint32_t SCRIPT_HASH = crc32("script");
|
||||
|
||||
|
||||
namespace Lumix
|
||||
{
|
||||
class ScriptSystemImpl;
|
||||
|
||||
class ScriptSceneImpl : public ScriptScene
|
||||
{
|
||||
public:
|
||||
typedef void (*InitFunction)(ScriptScene*);
|
||||
typedef void (*UpdateFunction)(float);
|
||||
typedef void (*DoneFunction)();
|
||||
typedef void (*SerializeFunction)(OutputBlob&);
|
||||
typedef void (*DeserializeFunction)(InputBlob&);
|
||||
|
||||
public:
|
||||
ScriptSceneImpl(ScriptSystemImpl& system, Engine& engine, Universe& universe)
|
||||
: m_universe(universe)
|
||||
, m_engine(engine)
|
||||
, m_system(system)
|
||||
, m_allocator(engine.getAllocator())
|
||||
, m_paths(m_allocator)
|
||||
, m_script_entities(m_allocator)
|
||||
, m_script_renamed(m_allocator)
|
||||
, m_library(NULL)
|
||||
, m_done_function(NULL)
|
||||
, m_deserialize_function(NULL)
|
||||
, m_serialize_function(NULL)
|
||||
, m_update_function(NULL)
|
||||
, m_reload_after_compile(false)
|
||||
{
|
||||
if (m_engine.getWorldEditor())
|
||||
{
|
||||
m_engine.getWorldEditor()->gameModeToggled().bind<ScriptSceneImpl, &ScriptSceneImpl::onGameModeToggled>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
~ScriptSceneImpl()
|
||||
{
|
||||
if (m_engine.getWorldEditor())
|
||||
{
|
||||
m_engine.getWorldEditor()->gameModeToggled().unbind<ScriptSceneImpl, &ScriptSceneImpl::onGameModeToggled>(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void onGameModeToggled(bool is_starting)
|
||||
{
|
||||
if (is_starting)
|
||||
{
|
||||
if (!m_library)
|
||||
{
|
||||
m_library = Library::create(m_library_path, m_allocator);
|
||||
if (!m_library->load())
|
||||
{
|
||||
g_log_error.log("script") << "Could not load " << m_library_path.c_str();
|
||||
Library::destroy(m_library);
|
||||
m_library = NULL;
|
||||
return;
|
||||
}
|
||||
m_update_function = (UpdateFunction)m_library->resolve("update");
|
||||
m_done_function = (DoneFunction)m_library->resolve("done");
|
||||
m_serialize_function = (SerializeFunction)m_library->resolve("serialize");
|
||||
m_deserialize_function = (DeserializeFunction)m_library->resolve("deserialize");
|
||||
InitFunction init_function = (InitFunction)m_library->resolve("init");
|
||||
if (!m_update_function || !init_function)
|
||||
{
|
||||
g_log_error.log("script") << "Script interface in " << m_library_path.c_str() << " is not complete";
|
||||
}
|
||||
|
||||
if (init_function)
|
||||
{
|
||||
init_function(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unloadLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual bool ownComponentType(uint32_t type) const override
|
||||
{
|
||||
return type == SCRIPT_HASH;
|
||||
}
|
||||
|
||||
|
||||
virtual IPlugin& getPlugin() const;
|
||||
|
||||
|
||||
void deserialize(InputBlob& serializer) override
|
||||
{
|
||||
int32_t count;
|
||||
serializer.read(count);
|
||||
m_script_entities.resize(count);
|
||||
m_paths.clear();
|
||||
m_paths.reserve(count);
|
||||
for (int i = 0; i < m_script_entities.size(); ++i)
|
||||
{
|
||||
serializer.read(m_script_entities[i]);
|
||||
char path[LUMIX_MAX_PATH];
|
||||
serializer.readString(path, sizeof(path));
|
||||
m_paths.push(Path(path));
|
||||
Entity entity(&m_universe, m_script_entities[i]);
|
||||
if(m_script_entities[i] != -1)
|
||||
{
|
||||
m_universe.addComponent(entity, SCRIPT_HASH, this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void serializeScripts(OutputBlob& blob) override
|
||||
{
|
||||
if (m_serialize_function)
|
||||
{
|
||||
m_serialize_function(blob);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void deserializeScripts(InputBlob& blob) override
|
||||
{
|
||||
if (m_deserialize_function)
|
||||
{
|
||||
m_deserialize_function(blob);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void update(float time_delta) override
|
||||
{
|
||||
if (m_is_compiling)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_update_function)
|
||||
{
|
||||
m_update_function(time_delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual const Lumix::Path& getScriptPath(Component cmp) override
|
||||
{
|
||||
return m_paths[cmp.index];
|
||||
}
|
||||
|
||||
|
||||
void getScriptPath(Component cmp, string& str) override
|
||||
{
|
||||
str = m_paths[cmp.index];
|
||||
}
|
||||
|
||||
|
||||
virtual DelegateList<void(const Path&, const Path&)>& scriptRenamed() override
|
||||
{
|
||||
return m_script_renamed;
|
||||
}
|
||||
|
||||
|
||||
void setScriptPath(Component cmp, const string& str) override
|
||||
{
|
||||
Lumix::Path old_path = m_paths[cmp.index];
|
||||
m_paths[cmp.index] = str.c_str();
|
||||
m_script_renamed.invoke(old_path, m_paths[cmp.index]);
|
||||
}
|
||||
|
||||
|
||||
virtual Component getNextScript(const Component& cmp) override
|
||||
{
|
||||
for (int i = cmp.index + 1; i < m_script_entities.size(); ++i)
|
||||
{
|
||||
if (m_script_entities[i] != -1)
|
||||
{
|
||||
return Component(Entity(&m_universe, m_script_entities[i]), SCRIPT_HASH, this, i);
|
||||
}
|
||||
}
|
||||
return Component::INVALID;
|
||||
}
|
||||
|
||||
|
||||
virtual Component getFirstScript() override
|
||||
{
|
||||
for (int i = 0; i < m_script_entities.size(); ++i)
|
||||
{
|
||||
if (m_script_entities[i] != -1)
|
||||
{
|
||||
return Component(Entity(&m_universe, m_script_entities[i]), SCRIPT_HASH, this, i);
|
||||
}
|
||||
}
|
||||
return Component::INVALID;
|
||||
}
|
||||
|
||||
|
||||
void getScriptDefaultPath(Entity e, char* path, char* full_path, int length, const char* ext)
|
||||
{
|
||||
char tmp[30];
|
||||
toCString(e.index, tmp, 30);
|
||||
|
||||
copyString(full_path, length, m_engine.getBasePath());
|
||||
catCString(full_path, length, "e");
|
||||
catCString(full_path, length, tmp);
|
||||
catCString(full_path, length, ".");
|
||||
catCString(full_path, length, ext);
|
||||
|
||||
copyString(path, length, "e");
|
||||
catCString(path, length, tmp);
|
||||
catCString(path, length, ".");
|
||||
catCString(path, length, ext);
|
||||
}
|
||||
|
||||
|
||||
virtual void setModulePath(const char* path) override
|
||||
{
|
||||
char tmp[LUMIX_MAX_PATH];
|
||||
copyString(tmp, sizeof(tmp), path);
|
||||
catCString(tmp, sizeof(tmp), ".dll");
|
||||
m_library_path = tmp;
|
||||
}
|
||||
|
||||
|
||||
virtual void afterScriptCompiled() override
|
||||
{
|
||||
if (!m_library && m_reload_after_compile)
|
||||
{
|
||||
m_library = Library::create(m_library_path, m_allocator);
|
||||
if (!m_library->load())
|
||||
{
|
||||
g_log_error.log("script") << "Could not load " << m_library_path.c_str();
|
||||
Library::destroy(m_library);
|
||||
m_library = NULL;
|
||||
return;
|
||||
}
|
||||
m_update_function = (UpdateFunction)m_library->resolve("update");
|
||||
m_done_function = (DoneFunction)m_library->resolve("done");
|
||||
m_serialize_function = (SerializeFunction)m_library->resolve("serialize");
|
||||
m_deserialize_function = (DeserializeFunction)m_library->resolve("deserialize");
|
||||
InitFunction init_function = (InitFunction)m_library->resolve("init");
|
||||
if (!m_update_function || !init_function)
|
||||
{
|
||||
g_log_error.log("script") << "Script interface in " << m_library_path.c_str() << " is not complete";
|
||||
}
|
||||
|
||||
if (init_function)
|
||||
{
|
||||
init_function(this);
|
||||
}
|
||||
}
|
||||
m_is_compiling = false;
|
||||
}
|
||||
|
||||
virtual void beforeScriptCompiled() override
|
||||
{
|
||||
m_reload_after_compile = true;
|
||||
m_is_compiling = true;
|
||||
unloadLibrary();
|
||||
}
|
||||
|
||||
|
||||
void unloadLibrary()
|
||||
{
|
||||
if (m_done_function)
|
||||
{
|
||||
m_done_function();
|
||||
}
|
||||
if (m_library)
|
||||
{
|
||||
m_update_function = nullptr;
|
||||
m_done_function = nullptr;
|
||||
Library::destroy(m_library);
|
||||
m_library = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component createScript(Entity entity)
|
||||
{
|
||||
char path[LUMIX_MAX_PATH];
|
||||
char full_path[LUMIX_MAX_PATH];
|
||||
getScriptDefaultPath(entity, path, full_path, LUMIX_MAX_PATH, "cpp");
|
||||
|
||||
m_script_entities.push(entity.index);
|
||||
m_paths.push(Path(path));
|
||||
|
||||
Component cmp = m_universe.addComponent(entity, SCRIPT_HASH, this, m_script_entities.size() - 1);
|
||||
m_universe.componentCreated().invoke(cmp);
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void serialize(OutputBlob& serializer) override
|
||||
{
|
||||
serializer.write((int32_t)m_script_entities.size());
|
||||
for (int i = 0; i < m_script_entities.size(); ++i)
|
||||
{
|
||||
serializer.write((int32_t)m_script_entities[i]);
|
||||
serializer.writeString(m_paths[i].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual Component createComponent(uint32_t type, const Entity& entity)
|
||||
{
|
||||
if (type == SCRIPT_HASH)
|
||||
{
|
||||
return createScript(entity);
|
||||
}
|
||||
return Component::INVALID;
|
||||
}
|
||||
|
||||
|
||||
virtual void destroyComponent(const Component& cmp)
|
||||
{
|
||||
m_script_entities[cmp.index] = -1;
|
||||
m_universe.destroyComponent(cmp);
|
||||
}
|
||||
|
||||
|
||||
virtual Engine& getEngine() override
|
||||
{
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
IAllocator& m_allocator;
|
||||
Array<int32_t> m_script_entities;
|
||||
Array<Path> m_paths;
|
||||
Universe& m_universe;
|
||||
Engine& m_engine;
|
||||
ScriptSystemImpl& m_system;
|
||||
Library* m_library;
|
||||
Path m_library_path;
|
||||
UpdateFunction m_update_function;
|
||||
DoneFunction m_done_function;
|
||||
SerializeFunction m_serialize_function;
|
||||
DeserializeFunction m_deserialize_function;
|
||||
bool m_is_compiling;
|
||||
bool m_reload_after_compile;
|
||||
|
||||
DelegateList<void(const Path&, const Path&)> m_script_renamed;
|
||||
};
|
||||
|
||||
class ScriptSystemImpl : public IPlugin
|
||||
{
|
||||
public:
|
||||
Engine& m_engine;
|
||||
BaseProxyAllocator m_allocator;
|
||||
|
||||
|
||||
ScriptSystemImpl(Engine& engine)
|
||||
: m_engine(engine)
|
||||
, m_allocator(engine.getAllocator())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual IScene* createScene(Universe& universe) override
|
||||
{
|
||||
return m_allocator.newObject<ScriptSceneImpl>(*this, m_engine, universe);
|
||||
}
|
||||
|
||||
|
||||
virtual void destroyScene(IScene* scene) override
|
||||
{
|
||||
m_allocator.deleteObject(scene);
|
||||
}
|
||||
|
||||
|
||||
virtual bool create() override
|
||||
{
|
||||
if (m_engine.getWorldEditor())
|
||||
{
|
||||
IAllocator& allocator = m_engine.getWorldEditor()->getAllocator();
|
||||
#error todo m_engine.getWorldEditor()->registerComponentType(...
|
||||
m_engine.getWorldEditor()->registerProperty("script", allocator.newObject<FilePropertyDescriptor<ScriptSceneImpl> >("source", (void (ScriptSceneImpl::*)(Component, string&))&ScriptSceneImpl::getScriptPath, &ScriptSceneImpl::setScriptPath, "Script (*.cpp)", allocator));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual void destroy() override
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual const char* getName() const override
|
||||
{
|
||||
return "script";
|
||||
}
|
||||
|
||||
}; // ScriptSystemImpl
|
||||
|
||||
|
||||
IPlugin& ScriptSceneImpl::getPlugin() const
|
||||
{
|
||||
return m_system;
|
||||
}
|
||||
|
||||
|
||||
extern "C" IPlugin* createPlugin(Engine& engine)
|
||||
{
|
||||
return engine.getAllocator().newObject<ScriptSystemImpl>(engine);
|
||||
}
|
||||
|
||||
} // ~namespace Lumix
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include "engine/core/lumix.h"
|
||||
#include "engine/core/string.h"
|
||||
#include "engine/iplugin.h"
|
||||
#include "engine/universe/universe.h"
|
||||
|
||||
|
||||
namespace Lumix
|
||||
{
|
||||
|
||||
|
||||
class Engine;
|
||||
class Path;
|
||||
|
||||
|
||||
class LUMIX_SCRIPT_API ScriptScene : public IScene
|
||||
{
|
||||
public:
|
||||
virtual void getScriptPath(Component cmp, string& str) = 0;
|
||||
virtual const Lumix::Path& getScriptPath(Component cmp) = 0;
|
||||
virtual void setScriptPath(Component cmp, const string& str) = 0;
|
||||
|
||||
virtual void serializeScripts(OutputBlob& blob) = 0;
|
||||
virtual void deserializeScripts(InputBlob& blob) = 0;
|
||||
|
||||
virtual void beforeScriptCompiled() = 0;
|
||||
virtual void afterScriptCompiled() = 0;
|
||||
|
||||
virtual DelegateList<void(const Path&, const Path&)>& scriptRenamed() = 0;
|
||||
|
||||
virtual Component getFirstScript() = 0;
|
||||
virtual Component getNextScript(const Component& cmp) = 0;
|
||||
virtual void setModulePath(const char* path) = 0;
|
||||
|
||||
virtual Engine& getEngine() = 0;
|
||||
};
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
LUMIX_SCRIPT_API IPlugin* createPlugin(Engine& engine);
|
||||
}
|
||||
|
||||
|
||||
} // ~namespace Lumix
|
|
@ -1,17 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
namespace Lumix
|
||||
{
|
||||
|
||||
|
||||
class ScriptVisitor
|
||||
{
|
||||
public:
|
||||
virtual ~ScriptVisitor() {}
|
||||
|
||||
virtual void visit(const char* name, float& value) = 0;
|
||||
};
|
||||
|
||||
|
||||
} // ~ namespace Lumix
|
Loading…
Reference in a new issue