Editor/plugin separation - closes #844

Compilable "no-studio" option - closes #845
This commit is contained in:
Mikulas Florek 2016-05-12 18:00:47 +02:00
parent e77425eb37
commit 6a108bbbb1
16 changed files with 1228 additions and 1620 deletions

View file

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

View file

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

View file

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

View 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);
}

View file

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

View file

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

View file

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

View 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);
}

View 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);
}

View file

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

View file

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

View 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);
}

View file

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

View file

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

View file

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

View file

@ -1,17 +0,0 @@
#pragma once
namespace Lumix
{
class ScriptVisitor
{
public:
virtual ~ScriptVisitor() {}
virtual void visit(const char* name, float& value) = 0;
};
} // ~ namespace Lumix