fixed game export; Lua API - support methods returning structures, e.g. RenderModuleImpl::getCameraRay

This commit is contained in:
Mikulas Florek 2023-10-13 15:18:09 +02:00
parent 47e344b302
commit a2567f98db
36 changed files with 500 additions and 485 deletions

View File

@ -41,8 +41,8 @@ declare ImGui: {
NewLine : () -> (),
NextColumn : () -> (),
OpenPopup : (string) -> (),
PlotLines : (string, {number}, Vec2) -> (),
PopItemWidth : () -> (),
PlotLines : (string, {number}, Vec2) -> (),
PopItemWidth : () -> (),
PopID : () -> (),
PopStyleColor : (number) -> (),
PopStyleVar : (number) -> (),
@ -97,8 +97,19 @@ end
declare class SweepHit
position : Vec3
normal : Vec3
entity : Entity
distance : number
entity : Entity
end
declare class Ray
origin : any
dir : Vec3
end
declare class RayCastModelHit
is_hit : boolean
t : number
entity : Entity
end
declare class GUISystem
@ -118,7 +129,7 @@ end
declare class Model
getBoneCount : (Model) -> number
getBoneName : (Model, number) -> string
getBoneName : (Model, number) -> any --[[char]]
getBoneParent : (Model, number) -> number
end
@ -134,7 +145,7 @@ end
declare class gui_module
getRectAt : (gui_module, Vec2) -> Entity?
isOver : (gui_module, Vec2, Entity?) -> boolean
getSystem : (gui_module) -> any --[[GUISystem *]]
getSystem : (gui_module) -> GUISystem
end
declare class lua_script_module
@ -154,6 +165,7 @@ declare class renderer_module
addDebugCross : (renderer_module, DVec3, number, Color) -> ()
addDebugLine : (renderer_module, DVec3, DVec3, Color) -> ()
addDebugTriangle : (renderer_module, DVec3, DVec3, DVec3, Color) -> ()
castRay : (renderer_module, any --[[void*]], Entity?) -> any --[[RayCastModelHit]]
setActiveCamera : (renderer_module, Entity?) -> ()
end
@ -207,6 +219,7 @@ declare class camera_component
far: number
orthographic: boolean
orthographic_size: number
getRay : (camera_component, Vec2) -> any --[[Ray]]
end
declare class decal_component
@ -248,7 +261,7 @@ declare class model_instance_component
enabled: boolean
material: string
source: string
getModel : (model_instance_component) -> any --[[Model *]]
getModel : (model_instance_component) -> Model
end
declare class environment_probe_component
@ -446,7 +459,7 @@ declare class navmesh_zone_component
drawNavmesh : (navmesh_zone_component, DVec3, boolean, boolean, boolean) -> ()
drawCompactHeightfield : (navmesh_zone_component) -> ()
drawHeightfield : (navmesh_zone_component) -> ()
generateNavmesh : (navmesh_zone_component) -> any --[[NavmeshBuildJob *]]
generateNavmesh : (navmesh_zone_component) -> any --[[NavmeshBuildJob]]
end
declare class lua_script_inline_component
@ -585,6 +598,8 @@ declare Editor: {
declare LumixAPI: {
RaycastHit : { create : () -> RaycastHit, destroy : (RaycastHit) -> () },
SweepHit : { create : () -> SweepHit, destroy : (SweepHit) -> () },
Ray : { create : () -> Ray, destroy : (Ray) -> () },
RayCastModelHit : { create : () -> RayCastModelHit, destroy : (RayCastModelHit) -> () },
INPUT_KEYCODE_SHIFT: number,
INPUT_KEYCODE_LEFT : number,

View File

@ -18,6 +18,7 @@
#include "engine/thread.h"
#include "engine/world.h"
#include "gui/gui_system.h"
#include "lua_script/lua_script.h"
#include "lua_script/lua_script_system.h"
#include "renderer/pipeline.h"
#include "renderer/render_module.h"
@ -79,7 +80,7 @@ struct Runner final
m_viewport.rot = Quat::IDENTITY;
m_renderer = static_cast<Renderer*>(m_engine->getSystemManager().getSystem("renderer"));
PipelineResource* pres = m_engine->getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
LuaScript* pres = m_engine->getResourceManager().load<LuaScript>(Path("pipelines/main.lua"));
m_pipeline = Pipeline::create(*m_renderer, pres, "APP");
while (m_engine->getFileSystem().hasWork()) {

View File

@ -165,6 +165,8 @@ struct AssetBrowserImpl : AssetBrowser {
}
~AssetBrowserImpl() override {
removePlugin(m_world_asset_plugin);
m_app.getAssetCompiler().removePlugin(m_world_asset_plugin);
m_app.removeAction(&m_focus_search);
m_app.removeAction(&m_toggle_ui);
m_app.removeAction(&m_back_action);

View File

@ -551,12 +551,12 @@ struct AssetCompilerImpl : AssetCompiler {
|| fs.getLastModified(dst_path) < fs.getLastModified(meta_path)
)
{
if (!getPlugin(res.getPath())) return ResourceManagerHub::LoadHook::Action::IMMEDIATE;
if (!m_init_finished) {
res.incRefCount();
m_on_init_load.push(&res);
return ResourceManagerHub::LoadHook::Action::DEFERRED;
}
if (!getPlugin(res.getPath())) return ResourceManagerHub::LoadHook::Action::IMMEDIATE;
pushToCompileQueue(Path(filepath));
return ResourceManagerHub::LoadHook::Action::DEFERRED;

View File

@ -35,10 +35,13 @@ struct LUMIX_EDITOR_API AssetCompiler {
virtual void addPlugin(IPlugin& plugin, Span<const char*> extensions) = 0;
virtual void removePlugin(IPlugin& plugin) = 0;
virtual bool compile(const Path& path) = 0;
// load meta for `res` and returns it as lua_State*. Must call lua_close on the state after you are done using it.
virtual lua_State* getMeta(const Path& res) = 0;
virtual void updateMeta(const Path& resource, Span<const u8> data) const = 0;
virtual const HashMap<FilePathHash, ResourceItem>& lockResources() = 0;
virtual void unlockResources() = 0;
// register non-`Resource` dependency, so we reload dependants in case something changes
// `Resource` dependencies are automatically handled elsewhere
virtual void registerDependency(const Path& included_from, const Path& dependency) = 0;
virtual void addResource(ResourceType type, const Path& path) = 0;
virtual bool writeCompiledResource(const Path& path, Span<const u8> data) = 0;

View File

@ -152,14 +152,12 @@ Axis collide(const ScaleGizmo& gizmo, const WorldView& view, const Gizmo::Config
const float scale = getScale(vp, gizmo.pos, cfg.scale);
const Vec3 pos(gizmo.pos - vp.pos);
DVec3 origin;
Vec3 dir;
const Vec2 mp = view.getMousePos();
vp.getRay(mp, origin, dir);
const Vec3 rel_origin = Vec3(origin - vp.pos);
const float x_dist = getLineSegmentDistance(rel_origin, dir, pos, pos + gizmo.x);
const float y_dist = getLineSegmentDistance(rel_origin, dir, pos, pos + gizmo.y);
const float z_dist = getLineSegmentDistance(rel_origin, dir, pos, pos + gizmo.z);
const Ray ray = vp.getRay(mp);
const Vec3 rel_origin = Vec3(ray.origin - vp.pos);
const float x_dist = getLineSegmentDistance(rel_origin, ray.dir, pos, pos + gizmo.x);
const float y_dist = getLineSegmentDistance(rel_origin, ray.dir, pos, pos + gizmo.y);
const float z_dist = getLineSegmentDistance(rel_origin, ray.dir, pos, pos + gizmo.z);
float influenced_dist = scale * INFLUENCE_DISTANCE;
@ -173,33 +171,31 @@ Axis collide(const RotationGizmo& gizmo, const WorldView& view, const Gizmo::Con
const Vec3 pos(gizmo.pos - vp.pos);
const float scale = getScale(vp, gizmo.pos, cfg.scale);
DVec3 origin;
Vec3 dir;
const Viewport viewport = view.getViewport();
const Vec2 mp = view.getMousePos();
viewport.getRay(mp, origin, dir);
const Vec3 rel_origin(origin - vp.pos);
const Ray ray = viewport.getRay(mp);
const Vec3 rel_origin(ray.origin - vp.pos);
float t;
float mint = FLT_MAX;
float d = FLT_MAX;
Axis axis = Axis::NONE;
if (getRayPlaneIntersecion(rel_origin, dir, pos, normalize(gizmo.x), t) && t > 0) {
const Vec3 p = rel_origin + dir * t;
if (getRayPlaneIntersecion(rel_origin, ray.dir, pos, normalize(gizmo.x), t) && t > 0) {
const Vec3 p = rel_origin + ray.dir * t;
mint = t;
d = length(p - pos);
axis = Axis::X;
}
if (getRayPlaneIntersecion(rel_origin, dir, pos, normalize(gizmo.y), t) && t < mint && t > 0) {
const Vec3 p = rel_origin + dir * t;
if (getRayPlaneIntersecion(rel_origin, ray.dir, pos, normalize(gizmo.y), t) && t < mint && t > 0) {
const Vec3 p = rel_origin + ray.dir * t;
d = length(p - pos);
mint = t;
axis = Axis::Y;
}
if (getRayPlaneIntersecion(rel_origin, dir, pos, normalize(gizmo.z), t) && t < mint && t > 0) {
const Vec3 p = rel_origin + dir * t;
if (getRayPlaneIntersecion(rel_origin, ray.dir, pos, normalize(gizmo.z), t) && t < mint && t > 0) {
const Vec3 p = rel_origin + ray.dir * t;
d = length(p - pos);
axis = Axis::Z;
}
@ -209,27 +205,25 @@ Axis collide(const RotationGizmo& gizmo, const WorldView& view, const Gizmo::Con
}
Axis collide(const TranslationGizmo& gizmo, const Transform& tr, const WorldView& view, const Gizmo::Config& cfg) {
DVec3 origin;
Vec3 dir;
const Viewport viewport = view.getViewport();
const Vec2 mp = view.getMousePos();
viewport.getRay(mp, origin, dir);
const Ray ray = viewport.getRay(mp);
const Vec3 rel_origin(origin - viewport.pos);
const Vec3 rel_origin(ray.origin - viewport.pos);
float t, tmin = FLT_MAX;
const Vec3 pos(gizmo.pos - viewport.pos);
bool hit = getRayTriangleIntersection(rel_origin, dir, pos, pos + gizmo.x * 0.5f, pos + gizmo.y * 0.5f, &t);
bool hit = getRayTriangleIntersection(rel_origin, ray.dir, pos, pos + gizmo.x * 0.5f, pos + gizmo.y * 0.5f, &t);
Axis transform_axis = Axis::NONE;
if (hit) {
tmin = t;
transform_axis = Axis::XY;
}
hit = getRayTriangleIntersection(rel_origin, dir, pos, pos + gizmo.y * 0.5f, pos + gizmo.z * 0.5f, &t);
hit = getRayTriangleIntersection(rel_origin, ray.dir, pos, pos + gizmo.y * 0.5f, pos + gizmo.z * 0.5f, &t);
if (hit && t < tmin) {
tmin = t;
transform_axis = Axis::YZ;
}
hit = getRayTriangleIntersection(rel_origin, dir, pos, pos + gizmo.x * 0.5f, pos + gizmo.z * 0.5f, &t);
hit = getRayTriangleIntersection(rel_origin, ray.dir, pos, pos + gizmo.x * 0.5f, pos + gizmo.z * 0.5f, &t);
if (hit && t < tmin) transform_axis = Axis::XZ;
if (transform_axis != Axis::NONE) return transform_axis;
@ -239,9 +233,9 @@ Axis collide(const TranslationGizmo& gizmo, const Transform& tr, const WorldView
const Vec3 x = is_global ? gizmo.x : tr.rot.rotate(Vec3(scale, 0, 0));
const Vec3 y = is_global ? gizmo.y : tr.rot.rotate(Vec3(0, scale, 0));
const Vec3 z = is_global ? gizmo.z : tr.rot.rotate(Vec3(0, 0, scale));
const float x_dist = getLineSegmentDistance(rel_origin, dir, pos, pos + x);
const float y_dist = getLineSegmentDistance(rel_origin, dir, pos, pos + y);
const float z_dist = getLineSegmentDistance(rel_origin, dir, pos, pos + z);
const float x_dist = getLineSegmentDistance(rel_origin, ray.dir, pos, pos + x);
const float y_dist = getLineSegmentDistance(rel_origin, ray.dir, pos, pos + y);
const float z_dist = getLineSegmentDistance(rel_origin, ray.dir, pos, pos + z);
const float influenced_dist = length(gizmo.x) * INFLUENCE_DISTANCE;
if (x_dist < y_dist && x_dist < z_dist && x_dist < influenced_dist) return Axis::X;
@ -252,10 +246,8 @@ Axis collide(const TranslationGizmo& gizmo, const Transform& tr, const WorldView
template <typename Gizmo>
DVec3 getMousePlaneIntersection(const WorldView& view, const Gizmo& gizmo, Axis transform_axis) {
const Viewport& vp = view.getViewport();
DVec3 origin;
Vec3 dir;
const Vec2 mouse_pos = view.getMousePos();
vp.getRay(mouse_pos, origin, dir);
const Ray ray = vp.getRay(mouse_pos);
bool is_two_axed = transform_axis == Axis::XZ || transform_axis == Axis::XY || transform_axis == Axis::YZ;
if (is_two_axed) {
Vec3 plane_normal;
@ -266,11 +258,11 @@ DVec3 getMousePlaneIntersection(const WorldView& view, const Gizmo& gizmo, Axis
default: ASSERT(false); break;
}
float t;
const Vec3 rel_origin = Vec3(origin - gizmo.pos);
if (getRayPlaneIntersecion(rel_origin, dir, Vec3(0), plane_normal, t)) {
return origin + dir * t;
const Vec3 rel_origin = Vec3(ray.origin - gizmo.pos);
if (getRayPlaneIntersecion(rel_origin, ray.dir, Vec3(0), plane_normal, t)) {
return ray.origin + ray.dir * t;
}
return origin;
return ray.origin;
}
Vec3 axis;
@ -280,8 +272,8 @@ DVec3 getMousePlaneIntersection(const WorldView& view, const Gizmo& gizmo, Axis
case Axis::Z: axis = normalize(gizmo.z); break;
default: ASSERT(false); return DVec3(0);
}
const Vec3 normal = cross(cross(dir, axis), dir);
const float d = dot(Vec3(origin - gizmo.pos), normal) / dot(axis, normal);
const Vec3 normal = cross(cross(ray.dir, axis), ray.dir);
const float d = dot(Vec3(ray.origin - gizmo.pos), normal) / dot(axis, normal);
return gizmo.pos + axis * d;
}
@ -725,16 +717,14 @@ bool box(u64 id, WorldView& view, Transform& tr, Vec3& half_extents, const Confi
const Viewport vp = view.getViewport();
const float scale = getScale(vp, tr.pos, cfg.scale) * 0.1f;
DVec3 origin;
Vec3 dir;
const Vec2 mp = view.getMousePos();
vp.getRay(mp, origin, dir);
const Ray ray = vp.getRay(mp);
const Vec3 pos = Vec3(origin - tr.pos);
const Vec3 pos = Vec3(ray.origin - tr.pos);
const Vec3 center = Vec3(tr.pos - vp.pos);
auto cube = [&](u32 color, Vec3 p, float& prev_t){
float t;
if (getRaySphereIntersection(pos, dir, p, scale * 1.414f, t) && (prev_t < 0 || t < prev_t)) {
if (getRaySphereIntersection(pos, ray.dir, p, scale * 1.414f, t) && (prev_t < 0 || t < prev_t)) {
renderCube(view, SELECTED_COLOR, center + p, scale, xn, yn, zn);
WorldView::Vertex* line = view.render(true, 2);
line[0].pos = center;

View File

@ -18,7 +18,7 @@ struct RenderInterface {
virtual ImTextureID loadTexture(const struct Path& path) = 0;
virtual bool isValid(ImTextureID texture) = 0;
virtual void unloadTexture(ImTextureID handle) = 0;
virtual WorldView::RayHit castRay(World& world, const DVec3& origin, const Vec3& dir, EntityPtr ignored) = 0;
virtual WorldView::RayHit castRay(World& world, const struct Ray& ray, EntityPtr ignored) = 0;
virtual Path getModelInstancePath(World& world, EntityRef entity) = 0;
virtual bool saveTexture(Engine& engine, const char* path_cstr, const void* pixels, int w, int h, bool upper_left_origin) = 0;
};

View File

@ -34,15 +34,13 @@ struct SplineEditorPlugin : SplineEditor, StudioApp::MousePlugin, PropertyGrid::
World* world = m_app.getWorldEditor().getWorld();
const EntityRef e = *getSplineEntity();
const Transform tr = world->getTransform(e);
DVec3 origin;
Vec3 dir;
const Viewport& vp = view.getViewport();
vp.getRay(Vec2((float)x, (float)y), origin, dir);
const Ray ray = vp.getRay(Vec2((float)x, (float)y));
for (const Vec3& point : spline->points) {
const DVec3 p = tr.pos + point;
float t;
const bool hovered = getRaySphereIntersection(Vec3(0), dir, Vec3(p - vp.pos), 0.1f, t);
const bool hovered = getRaySphereIntersection(Vec3(0), ray.dir, Vec3(p - vp.pos), 0.1f, t);
if (hovered) return true;
}
@ -59,10 +57,8 @@ struct SplineEditorPlugin : SplineEditor, StudioApp::MousePlugin, PropertyGrid::
}
void onMouseUp(WorldView& view, int x, int y, os::MouseButton button) override {
DVec3 origin;
Vec3 dir;
const Viewport& vp = view.getViewport();
vp.getRay(Vec2((float)x, (float)y), origin, dir);
const Ray ray = vp.getRay(Vec2((float)x, (float)y));
const EntityRef e = *getSplineEntity();
World* world = m_app.getWorldEditor().getWorld();
@ -73,7 +69,7 @@ struct SplineEditorPlugin : SplineEditor, StudioApp::MousePlugin, PropertyGrid::
for (const Vec3& point : spline->points) {
const DVec3 p = tr.pos + point;
float t;
const bool hovered = getRaySphereIntersection(Vec3(0), dir, Vec3(p - vp.pos), 0.1f, t);
const bool hovered = getRaySphereIntersection(Vec3(0), ray.dir, Vec3(p - vp.pos), 0.1f, t);
if (hovered) {
m_selected = i32(&point - spline->points.begin());
return;
@ -213,13 +209,11 @@ struct SplineEditorPlugin : SplineEditor, StudioApp::MousePlugin, PropertyGrid::
const Transform& tr = world.getTransform(e);
const DVec3 cam_pos = view.getViewport().pos;
const Vec3 offset = Vec3(tr.pos - cam_pos);
DVec3 origin;
Vec3 dir;
view.getViewport().getRay(view.getMousePos(), origin, dir);
const Ray ray = view.getViewport().getRay(view.getMousePos());
for (i32 i = 0; i < spline.points.size(); ++i) {
const DVec3 p = tr.pos + spline.points[i];
float t;
const bool hovered = getRaySphereIntersection(Vec3(0), dir, Vec3(p - cam_pos), 0.1f, t);
const bool hovered = getRaySphereIntersection(Vec3(0), ray.dir, Vec3(p - cam_pos), 0.1f, t);
addCircle(view, p, 0.1f, tr.rot.rotate(Vec3(0, 1, 0)), hovered ? Color::RED : Color::GREEN);
}

View File

@ -1194,12 +1194,12 @@ struct StudioAppImpl final : StudioApp
for (EntityRef entity : selected) {
const DVec3 origin = world->getPosition(entity);
auto hit = getRenderInterface()->castRay(*world, origin, Vec3(0, -1, 0), entity);
auto hit = getRenderInterface()->castRay(*world, Ray{origin, Vec3(0, -1, 0)}, entity);
if (hit.is_hit) {
new_positions.push(origin + Vec3(0, -hit.t, 0));
}
else {
hit = getRenderInterface()->castRay(*world, origin, Vec3(0, 1, 0), entity);
hit = getRenderInterface()->castRay(*world, Ray{origin, Vec3(0, 1, 0)}, entity);
if (hit.is_hit) {
new_positions.push(origin + Vec3(0, hit.t, 0));
}
@ -3105,8 +3105,6 @@ struct StudioAppImpl final : StudioApp
os::FileIterator* iter = m_engine->getFileSystem().createFileIterator(".lumix/resources");
const char* base_path = m_engine->getFileSystem().getBasePath();
os::FileInfo info;
exportDataScan("pipelines/", infos);
exportDataScan("universes/", infos);
exportFile("lumix.prj", infos);
while (os::getNextFile(iter, &info)) {
if (info.is_directory) continue;
@ -3123,6 +3121,8 @@ struct StudioAppImpl final : StudioApp
catString(rec.path, info.filename);
infos.insert(rec.hash, rec);
}
exportDataScan("pipelines/", infos);
exportDataScan("universes/", infos);
os::destroyFileIterator(iter);
}
@ -3188,9 +3188,6 @@ struct StudioAppImpl final : StudioApp
void exportDataScanResources(AssociativeArray<FilePathHash, ExportFileInfo>& infos)
{
ResourceManagerHub& rm = m_engine->getResourceManager();
exportDataScan("scripts/", infos);
exportDataScan("pipelines/", infos);
exportDataScan("universes/", infos);
exportFile("lumix.prj", infos);
for (auto iter = rm.getAll().begin(), end = rm.getAll().end(); iter != end; ++iter) {
const auto& resources = iter.value()->getResourceTable();
@ -3198,13 +3195,17 @@ struct StudioAppImpl final : StudioApp
const FilePathHash hash = res->getPath().getHash();
const Path baked_path(".lumix/resources/", hash, ".res");
auto& out_info = infos.emplace(hash);
copyString(Span(out_info.path), baked_path);
out_info.hash = hash;
out_info.size = os::getFileSize(baked_path);
out_info.offset = ~0UL;
if (infos.find(hash) < 0) {
auto& out_info = infos.emplace(hash);
copyString(Span(out_info.path), baked_path);
out_info.hash = hash;
out_info.size = os::getFileSize(baked_path);
out_info.offset = ~0UL;
}
}
}
exportDataScan("pipelines/", infos);
exportDataScan("universes/", infos);
}

View File

@ -709,13 +709,14 @@ Matrix Viewport::getViewRotation() const
}
void Viewport::getRay(const Vec2& screen_pos, DVec3& origin, Vec3& dir) const
Ray Viewport::getRay(const Vec2& screen_pos) const
{
origin = pos;
Ray ray;
ray.origin = pos;
if (w <= 0 || h <= 0) {
dir = rot.rotate(Vec3(0, 0, 1));
return;
ray.dir = rot.rotate(Vec3(0, 0, 1));
return ray;
}
const float nx = 2 * (screen_pos.x / w) - 1;
@ -727,19 +728,20 @@ void Viewport::getRay(const Vec2& screen_pos, DVec3& origin, Vec3& dir) const
const Vec3 x = rot * Vec3(1, 0, 0);
const Vec3 y = rot * Vec3(0, 1, 0);
float ratio = h > 0 ? w / (float)h : 1;
origin += x * nx * ortho_size * ratio
ray.origin += x * nx * ortho_size * ratio
+ y * ny * ortho_size;
}
const Matrix view_matrix = getView(origin);
const Matrix view_matrix = getView(ray.origin);
const Matrix inverted = (projection_matrix * view_matrix).inverted();
Vec4 p0 = inverted * Vec4(nx, ny, -1, 1);
Vec4 p1 = inverted * Vec4(nx, ny, 1, 1);
p0 *= 1 / p0.w;
p1 *= 1 / p1.w;
dir = normalize((p1 - p0).xyz());
if (is_ortho) dir *= -1.f;
ray.dir = normalize((p1 - p0).xyz());
if (is_ortho) ray.dir *= -1.f;
return ray;
}

View File

@ -182,7 +182,7 @@ struct LUMIX_ENGINE_API Viewport {
ShiftedFrustum getFrustum() const;
ShiftedFrustum getFrustum(const Vec2& viewport_min_px, const Vec2& viewport_max_px) const;
Vec2 worldToScreenPixels(const DVec3& world) const;
void getRay(const Vec2& screen_pos, DVec3& origin, Vec3& dir) const;
Ray getRay(const Vec2& screen_pos) const;
bool is_ortho;
float fov;

View File

@ -468,14 +468,14 @@ static i32 LUA_getNextModule(lua_State* L) {
i32 LUA_getThisTypeName(lua_State* L) {
reflection::FunctionBase* fn = LuaWrapper::checkArg<reflection::FunctionBase*>(L, 1);
StringView sv = fn->getThisTypeName();
StringView sv = fn->getThisType().type_name;
lua_pushlstring(L, sv.begin, sv.size());
return 1;
}
i32 LUA_getReturnTypeName(lua_State* L) {
reflection::FunctionBase* fn = LuaWrapper::checkArg<reflection::FunctionBase*>(L, 1);
StringView sv = fn->getReturnTypeName();
StringView sv = fn->getReturnType().type_name;
lua_pushlstring(L, sv.begin, sv.size());
return 1;
}
@ -561,7 +561,7 @@ static u32 LUA_getFunctionArgCount(reflection::FunctionBase* fnc) {
static i32 LUA_getFunctionReturnType(lua_State* L) {
auto* fnc = LuaWrapper::checkArg<reflection::FunctionBase*>(L, 1);
StringView name = fnc->getReturnTypeName();
StringView name = fnc->getReturnType().type_name;
lua_pushlstring(L, name.begin, name.size());
return 1;
}
@ -805,14 +805,6 @@ static int LUA_loadWorld(lua_State* L)
return 0;
}
static int finishrequire(lua_State* L)
{
if (lua_isstring(L, -1))
lua_error(L);
return 1;
}
static int LUA_loadstring(lua_State* L) {
const char* src = LuaWrapper::checkArg<const char*>(L, 1);
size_t bytecode_size;
@ -832,71 +824,6 @@ static int LUA_loadstring(lua_State* L) {
return 1;
}
static int LUA_require(lua_State* L) {
const char* name = luaL_checkstring(L, 1);
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
// return the module from the cache
lua_getfield(L, -1, name);
if (!lua_isnil(L, -1))
{
// L stack: _MODULES result
return finishrequire(L);
}
lua_pop(L, 1);
Engine* engine = LuaWrapper::getClosureObject<Engine>(L);
Path path(name, ".lua");
OutputMemoryStream blob(engine->getAllocator());
if (!engine->getFileSystem().getContentSync(path, blob)) {
luaL_argerrorL(L, 1, "error loading module");
}
// module needs to run in a new thread, isolated from the rest
// note: we create ML on main thread so that it doesn't inherit environment of L
lua_State* GL = lua_mainthread(L);
lua_State* ML = lua_newthread(GL);
lua_xmove(GL, L, 1);
// new thread needs to have the globals sandboxed
luaL_sandboxthread(ML);
// now we can compile & run module on the new thread
size_t bytecode_size;
char* bytecode = luau_compile((const char*)blob.data(), blob.size(), nullptr, &bytecode_size);
if (luau_load(ML, name, bytecode, bytecode_size, 0) == 0)
{
int status = lua_resume(ML, L, 0);
if (status == 0)
{
if (lua_gettop(ML) == 0)
lua_pushstring(ML, "module must return a value");
else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1))
lua_pushstring(ML, "module must return a table or function");
}
else if (status == LUA_YIELD)
{
lua_pushstring(ML, "module can not yield");
}
else if (!lua_isstring(ML, -1))
{
lua_pushstring(ML, "unknown error while running module");
}
}
free(bytecode);
// there's now a return value on top of ML; L stack: _MODULES ML
lua_xmove(ML, L, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -4, name);
// L stack: _MODULES ML result
return finishrequire(L);
}
static int LUA_instantiatePrefab(lua_State* L) {
Engine* engine = LuaWrapper::getClosureObject<Engine>(L);
LuaWrapper::checkTableArg(L, 1);
@ -927,8 +854,6 @@ static int LUA_instantiatePrefab(lua_State* L) {
void registerEngineAPI(lua_State* L, Engine* engine)
{
lua_pushlightuserdata(L, engine);
lua_pushcclosure(L, &LUA_require, "require", 1);
lua_setglobal(L, "require");
lua_pushcfunction(L, &LUA_loadstring, "loadstring");
lua_setglobal(L, "loadstring");

View File

@ -71,7 +71,6 @@ bool pcall(lua_State* L, int nargs, int nres)
return true;
}
bool execute(lua_State* L
, StringView content
, const char* name

View File

@ -12,6 +12,7 @@ namespace Lumix {
struct World;
struct CameraParams;
struct PipelineTexture;
template <typename T> struct Array;
namespace LuaWrapper {

View File

@ -9,6 +9,8 @@ template <typename T> struct IsSame<T, T> { static constexpr bool Value = true;
template <typename T> struct RemoveReference { using Type = T; };
template <typename T> struct RemoveReference<T&> { using Type = T; };
template <typename T> struct RemoveReference<T&&> { using Type = T; };
template <typename T> struct RemovePointerHelper { using Type = T; };
template <typename T> struct RemovePointerHelper<T*> { using Type = T; };
template <typename T> struct RemoveConst { using Type = T; };
template <typename T> struct RemoveConst<const T> { using Type = T; };
template <typename T> struct RemoveConst<const T&> { using Type = T; };
@ -16,6 +18,7 @@ template <typename T> struct RemoveVolatile { using Type = T; };
template <typename T> struct RemoveVolatile<volatile T> { using Type = T; };
template <typename T> using RemoveCR = typename RemoveConst<typename RemoveReference<T>::Type>::Type;
template <typename T> using RemoveCVR = typename RemoveVolatile<RemoveCR<T>>::Type;
template <typename T> using RemovePointer = typename RemovePointerHelper<T>::Type;
template <int... T> struct Indices {};

View File

@ -7,13 +7,22 @@
#include "engine/string.h"
#include "engine/world.h"
namespace Lumix
{
namespace Lumix::reflection {
namespace detail {
StringView normalizeTypeName(StringView type_name) {
StringView res = type_name;
if (startsWith(res, "struct ")) res.removePrefix(7);
if (startsWith(res, "Lumix::")) res.removePrefix(7);
while (res.size() > 0 && res[0] == ' ') res.removePrefix(1);
while (res.size() > 0 && res.back() == ' ') res.removeSuffix(1);
return res;
}
} // namespace detail
namespace reflection
{
struct Context {
Module* first_module = nullptr;
RegisteredComponent component_bases[ComponentType::MAX_TYPES_COUNT];
@ -298,7 +307,4 @@ void ArrayProperty::visitChildren(struct IPropertyVisitor& visitor) const {
}
}
} // namespace Reflection
} // namespace Lumix
} // namespace Lumix::reflection

View File

@ -307,6 +307,8 @@ struct GetTypeNameHelper
}
};
LUMIX_ENGINE_API StringView normalizeTypeName(StringView type_name);
} // namespace detail
@ -321,7 +323,7 @@ const IAttribute* getAttribute(const Property<T>& prop, IAttribute::Type type) {
template <typename T>
StringView getTypeName()
{
return detail::GetTypeNameHelper<T>::GetTypeName();
return detail::normalizeTypeName(detail::GetTypeNameHelper<T>::GetTypeName());
}
struct Variant {
@ -372,9 +374,14 @@ struct Variant {
};
struct TypeDescriptor {
using Copier = void* (*)(const void* src, IAllocator& allocator);
Variant::Type type;
StringView type_name;
bool is_const;
bool is_reference;
bool is_pointer;
u32 size;
Copier create_copy;
};
template <typename T> struct VariantTag {};
@ -399,9 +406,23 @@ template <typename T> inline Variant::Type getVariantType() { return _getVariant
template <typename T> TypeDescriptor toTypeDescriptor() {
TypeDescriptor td;
td.create_copy = [](const void* src, IAllocator& allocator) -> void* {
if constexpr (__is_constructible(T)) {
return LUMIX_NEW(allocator, RemoveCVR<T>)(*(RemoveCVR<T>*)src);
}
return nullptr;
};
td.type_name = getTypeName<RemoveCVR<RemovePointer<T>>>();
td.type = getVariantType<T>();
td.is_const = !IsSame<T, typename RemoveConst<T>::Type>::Value;
td.is_reference = !IsSame<T, typename RemoveReference<T>::Type>::Value;
td.is_pointer = !IsSame<T, RemovePointer<T>>::Value;
if constexpr (IsSame<T, void>::Value) {
td.size = 0;
}
else {
td.size = sizeof(T);
}
return td;
}
@ -411,10 +432,9 @@ struct FunctionBase
virtual u32 getArgCount() const = 0;
virtual TypeDescriptor getReturnType() const = 0;
virtual StringView getReturnTypeName() const = 0;
virtual StringView getThisTypeName() const = 0;
virtual TypeDescriptor getThisType() const = 0;
virtual TypeDescriptor getArgType(int i) const = 0;
virtual Variant invoke(void* obj, Span<Variant> args) const = 0;
virtual void invoke(void* obj, Span<u8> ret_mem, Span<Variant> args) const = 0;
virtual bool isConstMethod() = 0;
const char* decl_code;
@ -441,18 +461,16 @@ template <typename T> inline T& fromVariant(int i, Span<Variant> args, VariantTa
template <typename... Args>
struct VariantCaller {
template <typename C, typename F, int... I>
static Variant call(C* inst, F f, Span<Variant> args, Indices<I...>& indices) {
static void call(C* inst, F f, Span<u8> ret_mem, Span<Variant> args, Indices<I...>& indices) {
using R = typename ResultOf<F>::Type;
if constexpr (IsSame<R, void>::Value) {
Variant v;
v.type = Variant::VOID;
(inst->*f)(fromVariant(I, args, VariantTag<RemoveCVR<Args>>{})...);
return v;
}
else {
Variant v;
v = (inst->*f)(fromVariant(I, args, VariantTag<RemoveCVR<Args>>{})...);
return v;
auto v = (inst->*f)(fromVariant(I, args, VariantTag<RemoveCVR<Args>>{})...);
if (ret_mem.length() == sizeof(v)) {
memcpy(ret_mem.m_begin, &v, sizeof(v));
}
}
}
};
@ -522,8 +540,7 @@ struct Function<R (C::*)(Args...)> : FunctionBase
u32 getArgCount() const override { return sizeof...(Args); }
TypeDescriptor getReturnType() const override { return toTypeDescriptor<R>(); }
StringView getReturnTypeName() const override { return getTypeName<R>(); }
StringView getThisTypeName() const override { return getTypeName<C>(); }
TypeDescriptor getThisType() const override { return toTypeDescriptor<C>(); }
bool isConstMethod() override { return false; }
TypeDescriptor getArgType(int i) const override
@ -535,9 +552,9 @@ struct Function<R (C::*)(Args...)> : FunctionBase
return expand[i];
}
Variant invoke(void* obj, Span<Variant> args) const override {
void invoke(void* obj, Span<u8> ret_mem, Span<Variant> args) const override {
auto indices = typename BuildIndices<-1, sizeof...(Args)>::result{};
return VariantCaller<Args...>::call((C*)obj, function, args, indices);
VariantCaller<Args...>::call((C*)obj, function, ret_mem, args, indices);
}
};
@ -549,8 +566,7 @@ struct Function<R (C::*)(Args...) const> : FunctionBase
u32 getArgCount() const override { return sizeof...(Args); }
TypeDescriptor getReturnType() const override { return toTypeDescriptor<R>(); }
StringView getReturnTypeName() const override { return getTypeName<R>(); }
StringView getThisTypeName() const override { return getTypeName<C>(); }
TypeDescriptor getThisType() const override { return toTypeDescriptor<C>(); }
bool isConstMethod() override { return true; }
TypeDescriptor getArgType(int i) const override
@ -562,9 +578,9 @@ struct Function<R (C::*)(Args...) const> : FunctionBase
return expand[i];
}
Variant invoke(void* obj, Span<Variant> args) const override {
void invoke(void* obj, Span<u8> ret_mem, Span<Variant> args) const override {
auto indices = typename BuildIndices<-1, sizeof...(Args)>::result{};
return VariantCaller<Args...>::call((const C*)obj, function, args, indices);
VariantCaller<Args...>::call((const C*)obj, function, ret_mem, args, indices);
}
};

View File

@ -20,6 +20,7 @@
#include "engine/world.h"
#include "gui/gui_module.h"
#include "gui/sprite.h"
#include "lua_script/lua_script.h"
#include "renderer/draw2d.h"
#include "renderer/gpu/gpu.h"
#include "renderer/pipeline.h"
@ -244,7 +245,7 @@ public:
void init() {
Engine& engine = m_app.getEngine();
Renderer& renderer = *static_cast<Renderer*>(engine.getSystemManager().getSystem("renderer"));
PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/gui_editor.pln"));
LuaScript* pres = engine.getResourceManager().load<LuaScript>(Path("pipelines/gui_editor.lua"));
m_pipeline = Pipeline::create(renderer, pres, "");
}

View File

@ -649,6 +649,60 @@ struct EditorWindow : AssetEditorWindow {
#endif
};
static bool gatherRequires(Span<const u8> src, Lumix::Array<Path>& dependencies) {
lua_State* L = luaL_newstate();
auto reg_dep = [](lua_State* L) -> int {
lua_getglobal(L, "__deps");
Lumix::Array<Path>* deps = (Lumix::Array<Path>*)lua_tolightuserdata(L, -1);
lua_pop(L, 1);
const char* path = LuaWrapper::checkArg<const char*>(L, 1);
Path lua_path(path, ".lua");
deps->push(lua_path);
return 0;
};
auto index_fn = [](lua_State* L) -> int {
lua_insert(L, 1);
return 1;
};
auto call_fn = [](lua_State* L) -> int {
lua_insert(L, 1);
return 1;
};
lua_pushcclosure(L, reg_dep, "require", 0);
lua_setfield(L, LUA_GLOBALSINDEX, "require");
lua_pushlightuserdata(L, &dependencies);
lua_setfield(L, LUA_GLOBALSINDEX, "__deps");
lua_newtable(L); // metatable
lua_pushcfunction(L, index_fn, "__index"); // metatable, fn
lua_setfield(L, -2, "__index"); // metatable
lua_pushcfunction(L, call_fn, "__call"); // metatable, fn
lua_setfield(L, -2, "__call"); // metatable
lua_newtable(L); // metatable, new_g
lua_getglobal(L, "require"); // metatable, new_g, require
lua_setfield(L, -2, "require"); // metatable, new_g
lua_insert(L, -2); // new_g, meta
lua_setmetatable(L, -2); //new_g
bool errors = LuaWrapper::luaL_loadbuffer(L, (const char*)src.m_begin, src.length(), "gather_requires"); // new_g, fn
if (errors) {
lua_close(L);
return false;
}
lua_insert(L, -2); // fn, new_g
lua_setfenv(L, -2);
const bool res = LuaWrapper::pcall(L, 0, 0);
lua_close(L);
return res;
}
struct AssetPlugin : AssetBrowser::IPlugin, AssetCompiler::IPlugin {
explicit AssetPlugin(LuauAnalysis& analysis, StudioApp& app)
@ -664,7 +718,23 @@ struct AssetPlugin : AssetBrowser::IPlugin, AssetCompiler::IPlugin {
m_app.getAssetBrowser().addWindow(win.move());
}
bool compile(const Path& src) override { return m_app.getAssetCompiler().copyCompile(src); }
bool compile(const Path& src) override {
FileSystem& fs = m_app.getEngine().getFileSystem();
OutputMemoryStream src_data(m_app.getAllocator());
if (!fs.getContentSync(src, src_data)) return false;
Array<Path> deps(m_app.getAllocator());
if (!gatherRequires(src_data, deps)) return false;
OutputMemoryStream out(m_app.getAllocator());
out.write(deps.size());
for (const Path& dep : deps) {
out.writeString(dep.c_str());
}
out.write(src_data.data(), src_data.size());
return m_app.getAssetCompiler().writeCompiledResource(src, out);
}
const char* getLabel() const override { return "Lua script"; }
bool canCreateResource() const override { return true; }
const char* getDefaultExtension() const override { return "lua"; }

View File

@ -1,5 +1,7 @@
#include "engine/log.h"
#include "engine/file_system.h"
#include "engine/resource_manager.h"
#include "engine/stream.h"
#include "lua_script.h"
namespace Lumix {
@ -10,15 +12,29 @@ LuaScript::LuaScript(const Path& path, ResourceManager& resource_manager, IAlloc
: Resource(path, resource_manager, allocator)
, m_allocator(allocator, m_path.c_str())
, m_source_code(m_allocator)
, m_dependencies(m_allocator)
{
}
LuaScript::~LuaScript() = default;
void LuaScript::unload() { m_source_code = ""; }
void LuaScript::unload() {
for (LuaScript* scr : m_dependencies) scr->decRefCount();
m_dependencies.clear();
m_source_code = "";
}
bool LuaScript::load(Span<const u8> mem) {
m_source_code = StringView((const char*)mem.begin(), mem.length());
InputMemoryStream blob(mem.begin(), mem.length());
u32 num_deps;
blob.read(num_deps);
for (u32 i = 0; i < num_deps; ++i) {
const char* dep_path = blob.readString();
LuaScript* scr = m_resource_manager.getOwner().load<LuaScript>(Path(dep_path));
addDependency(*scr);
m_dependencies.push(scr);
}
m_source_code = StringView((const char*)blob.skip(0), (u32)blob.remaining());
return true;
}

View File

@ -26,6 +26,7 @@ public:
private:
TagAllocator m_allocator;
Array<LuaScript*> m_dependencies;
String m_source_code;
};

View File

@ -18,6 +18,7 @@
#include "engine/world.h"
#include "gui/gui_module.h"
#include "lua_script/lua_script.h"
#include <luacode.h>
namespace Lumix {
@ -107,15 +108,6 @@ struct ArrayItemSetVisitor : reflection::IPropertyVisitor {
lua_State* L;
};
static void pushObject(lua_State* L, void* obj, StringView type_name) {
ASSERT(!type_name.empty());
const char* c = type_name.end - 1;
while (*c != ':' && c != type_name.begin) --c;
if (*c == ':') ++c;
LuaWrapper::pushObject(L, obj, StringView(c, u32(type_name.end - c - 2)));
}
static void toVariant(reflection::Variant::Type type, lua_State* L, int idx, reflection::Variant& val) {
switch(type) {
case reflection::Variant::BOOL: val = LuaWrapper::checkArg<bool>(L, idx); break;
@ -150,24 +142,57 @@ static void toVariant(reflection::Variant::Type type, lua_State* L, int idx, ref
}
}
static int push(lua_State* L, const reflection::Variant& v, StringView type_name) {
switch (v.type) {
static bool isPath(const reflection::TypeDescriptor& type) {
if (type.type != reflection::Variant::CSTR) return false;
return equalStrings(type.type_name, "Path");
}
static int push(lua_State* L, Span<u8> val, const reflection::TypeDescriptor& type, World* world) {
#define RET(T) do { \
T v; \
ASSERT(sizeof(v) == val.length()); \
memcpy(&v, val.m_begin, sizeof(v)); \
LuaWrapper::push(L, v); \
return 1; \
} while(false) \
switch (type.type) {
default:
case reflection::Variant::ENTITY: ASSERT(false); return 0;
case reflection::Variant::VOID: return 0;
case reflection::Variant::BOOL: LuaWrapper::push(L, v.b); return 1;
case reflection::Variant::U32: LuaWrapper::push(L, v.u); return 1;
case reflection::Variant::I32: LuaWrapper::push(L, v.i); return 1;
case reflection::Variant::FLOAT: LuaWrapper::push(L, v.f); return 1;
case reflection::Variant::CSTR: LuaWrapper::push(L, v.s); return 1;
case reflection::Variant::VEC2: LuaWrapper::push(L, v.v2); return 1;
case reflection::Variant::BOOL: RET(bool);
case reflection::Variant::U32: RET(u32);
case reflection::Variant::I32: RET(i32);
case reflection::Variant::VEC2: RET(Vec2);
case reflection::Variant::COLOR:
case reflection::Variant::VEC3: LuaWrapper::push(L, v.v3); return 1;
case reflection::Variant::DVEC3: LuaWrapper::push(L, v.dv3); return 1;
case reflection::Variant::QUAT: LuaWrapper::push(L, v.quat); return 1;
case reflection::Variant::PTR: pushObject(L, v.ptr, type_name); return 1;
case reflection::Variant::VEC3: RET(Vec3);
case reflection::Variant::DVEC3: RET(DVec3);
case reflection::Variant::QUAT: RET(Quat);
case reflection::Variant::PTR: {
if (type.is_pointer) {
void* ptr;
ASSERT(sizeof(ptr) == val.length());
memcpy(&ptr, val.m_begin, sizeof(ptr));
LuaWrapper::pushObject(L, ptr, type.type_name);
return 1;
}
void* inst = type.create_copy(val.m_begin, getGlobalAllocator());
LuaWrapper::pushObject(L, inst, type.type_name);
return 1;
}
case reflection::Variant::FLOAT: RET(float);
case reflection::Variant::CSTR: {
if (isPath(type)) {
LuaWrapper::push(L, (const char*)val.m_begin);
return 1;
}
RET(const char*);
}
}
ASSERT(false);
return 0;
#undef RET
}
static int luaMethodClosure(lua_State* L) {
@ -189,8 +214,12 @@ static int luaMethodClosure(lua_State* L) {
toVariant(type, L, i + 2, args[i]);
}
const reflection::Variant res = f->invoke(obj, Span(args, f->getArgCount()));
return push(L, res, f->getReturnTypeName());
u8 res_mem[128];
reflection::TypeDescriptor ret_type = f->getReturnType();
ASSERT(ret_type.size <= sizeof(res_mem));
Span<u8> res(res_mem, ret_type.size);
f->invoke(obj, res, Span(args, f->getArgCount()));
return push(L, res, f->getReturnType(), nullptr);
}
static int luaModuleMethodClosure(lua_State* L) {
@ -208,12 +237,14 @@ static int luaModuleMethodClosure(lua_State* L) {
reflection::Variant::Type type = f->getArgType(i).type;
toVariant(type, L, i + 2, args[i]);
}
const reflection::Variant res = f->invoke(module, Span(args, f->getArgCount()));
if (res.type == reflection::Variant::ENTITY) {
LuaWrapper::pushEntity(L, res.e, &module->getWorld());
return 1;
}
return push(L, res, f->getReturnTypeName());
u8 res_mem[128];
reflection::TypeDescriptor ret_type = f->getReturnType();
ASSERT(ret_type.size <= sizeof(res_mem));
Span<u8> res(res_mem, ret_type.size);
f->invoke(module, res, Span(args, f->getArgCount()));
return push(L, res, f->getReturnType(), &module->getWorld());
}
static int luaCmpMethodClosure(lua_State* L) {
@ -242,12 +273,14 @@ static int luaCmpMethodClosure(lua_State* L) {
reflection::Variant::Type type = f->getArgType(i).type;
toVariant(type, L, i + 1, args[i]);
}
const reflection::Variant res = f->invoke(module, Span(args, f->getArgCount()));
if (res.type == reflection::Variant::ENTITY) {
LuaWrapper::pushEntity(L, res.e, &module->getWorld());
return 1;
}
return push(L, res, f->getReturnTypeName());
u8 res_mem[sizeof(Path)];
reflection::TypeDescriptor ret_type = f->getReturnType();
ASSERT(ret_type.size <= sizeof(res_mem));
Span<u8> res(res_mem, ret_type.size);
f->invoke(module, res, Span(args, f->getArgCount()));
return push(L, res, f->getReturnType(), &module->getWorld());
}
static int lua_struct_var_setter(lua_State* L) {
@ -305,6 +338,10 @@ static int lua_struct_var_getter(lua_State* L) {
LuaWrapper::push(L, var->get<DVec3>(inst));
return 1;
}
case reflection::Variant::BOOL: {
LuaWrapper::push(L, var->get<bool>(inst));
return 1;
}
case reflection::Variant::VEC3: {
LuaWrapper::push(L, var->get<Vec3>(inst));
return 1;
@ -313,6 +350,10 @@ static int lua_struct_var_getter(lua_State* L) {
LuaWrapper::push(L, var->get<float>(inst));
return 1;
}
case reflection::Variant::ENTITY: {
LuaWrapper::push(L, var->get<EntityPtr>(inst).index);
return 1;
}
default:
ASSERT(false);
// TODO
@ -374,12 +415,8 @@ static void createClasses(lua_State* L) {
}
for (auto* f : reflection::allFunctions()) {
char tmp_obj_type_name[128];
copyString(Span(tmp_obj_type_name), f->getThisTypeName());
const char* c = tmp_obj_type_name + strlen(tmp_obj_type_name);
while (*c != ':' && c != tmp_obj_type_name) --c;
if (*c == ':') ++c;
const char* obj_type_name = c;
char obj_type_name[128];
copyString(Span(obj_type_name), f->getThisType().type_name);
if (LuaWrapper::getField(L, -1, obj_type_name) != LUA_TTABLE) { // [LumixAPI, obj|nil ]
lua_pop(L, 1); // [LumixAPI]
lua_newtable(L); // [LumixAPI, obj]
@ -1534,18 +1571,9 @@ public:
for (const reflection::FunctionBase* f : module->functions) {
lua_pushlightuserdata(L, (void*)f); // [module, f]
if (f->name) {
lua_pushcclosure(L, luaModuleMethodClosure, f->name, 1);
lua_setfield(L, -2, f->name); // [module]
}
else {
const char* c = f->decl_code;
while (*c != ':' && *c) ++c;
ASSERT(*c == ':');
c += 2;
lua_pushcclosure(L, luaModuleMethodClosure, c, 1);
lua_setfield(L, -2, c); // [module]
}
ASSERT(f->name);
lua_pushcclosure(L, luaModuleMethodClosure, f->name, 1);
lua_setfield(L, -2, f->name); // [module]
}
lua_pop(L, 1); // []
@ -2765,12 +2793,90 @@ struct LuaProperties : reflection::DynamicProperties {
}
};
static int finishrequire(lua_State* L)
{
if (lua_isstring(L, -1))
lua_error(L);
return 1;
}
static int LUA_require(lua_State* L) {
const char* name = luaL_checkstring(L, 1);
luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1);
// return the module from the cache
lua_getfield(L, -1, name);
if (!lua_isnil(L, -1))
{
// L stack: _MODULES result
return finishrequire(L);
}
lua_pop(L, 1);
Engine* engine = LuaWrapper::getClosureObject<Engine>(L);
Path path(name, ".lua");
LuaScript* dep = engine->getResourceManager().load<LuaScript>(path);
if (!dep->isReady()) {
ASSERT(false); // require-d modules should be registered as dependencies, so it should be impossible to get here
luaL_argerrorL(L, 1, "error loading module");
}
// module needs to run in a new thread, isolated from the rest
// note: we create ML on main thread so that it doesn't inherit environment of L
lua_State* GL = lua_mainthread(L);
lua_State* ML = lua_newthread(GL);
lua_xmove(GL, L, 1);
// new thread needs to have the globals sandboxed
luaL_sandboxthread(ML);
// now we can compile & run module on the new thread
size_t bytecode_size;
char* bytecode = luau_compile((const char*)dep->getSourceCode().begin, dep->getSourceCode().size(), nullptr, &bytecode_size);
if (luau_load(ML, name, bytecode, bytecode_size, 0) == 0)
{
int status = lua_resume(ML, L, 0);
if (status == 0)
{
if (lua_gettop(ML) == 0)
lua_pushstring(ML, "module must return a value");
else if (!lua_istable(ML, -1) && !lua_isfunction(ML, -1))
lua_pushstring(ML, "module must return a table or function");
}
else if (status == LUA_YIELD)
{
lua_pushstring(ML, "module can not yield");
}
else if (!lua_isstring(ML, -1))
{
lua_pushstring(ML, "unknown error while running module");
}
}
free(bytecode);
// there's now a return value on top of ML; L stack: _MODULES ML
lua_xmove(ML, L, 1);
lua_pushvalue(L, -1);
lua_setfield(L, -4, name);
// L stack: _MODULES ML result
return finishrequire(L);
}
LuaScriptSystemImpl::LuaScriptSystemImpl(Engine& engine)
: m_engine(engine)
, m_allocator(engine.getAllocator(), "lua system")
, m_script_manager(m_allocator)
{
lua_State* L = engine.getState();
lua_pushlightuserdata(L, &engine);
lua_pushcclosure(L, &LUA_require, "require", 1);
lua_setglobal(L, "require");
m_script_manager.create(LuaScript::TYPE, engine.getResourceManager());
LUMIX_MODULE(LuaScriptModuleImpl, "lua_script")

View File

@ -15,6 +15,7 @@
#include "engine/resource_manager.h"
#include "engine/world.h"
#include "gui/gui_system.h"
#include "lua_script/lua_script.h"
#include "renderer/gpu/gpu.h"
#include "renderer/pipeline.h"
#include "renderer/render_module.h"
@ -68,7 +69,7 @@ void GameView::init() {
Engine& engine = m_app.getEngine();
auto* renderer = (Renderer*)engine.getSystemManager().getSystem("renderer");
PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
LuaScript* pres = engine.getResourceManager().load<LuaScript>(Path("pipelines/main.lua"));
m_pipeline = Pipeline::create(*renderer, pres, "GAME_VIEW");
auto* gui = static_cast<GUISystem*>(engine.getSystemManager().getSystem("gui"));

View File

@ -39,6 +39,7 @@
#include "engine/world.h"
#include "fbx_importer.h"
#include "game_view.h"
#include "lua_script/lua_script.h"
#include "model_meta.h"
#include "renderer/culling_system.h"
#include "renderer/editor/composite_texture.h"
@ -659,80 +660,6 @@ struct FontPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin
};
struct PipelinePlugin final : AssetCompiler::IPlugin, AssetBrowser::IPlugin {
struct EditorWindow : AssetEditorWindow {
EditorWindow(const Path& path, StudioApp& app, IAllocator& allocator)
: AssetEditorWindow(app)
, m_app(app)
{
m_resource = app.getEngine().getResourceManager().load<PipelineResource>(path);
}
~EditorWindow() {
m_resource->decRefCount();
}
void save() {
OutputMemoryStream blob(m_app.getAllocator());
m_editor->serializeText(blob);
m_app.getAssetBrowser().saveResource(m_resource->getPath(), blob);
m_dirty = false;
}
bool onAction(const Action& action) override {
if (&action == &m_app.getCommonActions().save) save();
else return false;
return true;
}
void windowGUI() override {
if (ImGui::BeginMenuBar()) {
if (ImGuiEx::IconButton(ICON_FA_SAVE, "Save")) save();
if (ImGuiEx::IconButton(ICON_FA_EXTERNAL_LINK_ALT, "Open externally")) m_app.getAssetBrowser().openInExternalEditor(m_resource);
if (ImGuiEx::IconButton(ICON_FA_SEARCH, "View in browser")) m_app.getAssetBrowser().locate(*m_resource);
ImGui::EndMenuBar();
}
if (m_resource->isEmpty()) {
ImGui::TextUnformatted("Loading...");
return;
}
if (!m_editor) {
m_editor = createLuaCodeEditor(m_app);
m_editor->setText(m_resource->content);
}
ImGui::PushFont(m_app.getMonospaceFont());
if (m_editor->gui("codeeditor")) m_dirty = true;
ImGui::PopFont();
}
const Path& getPath() override { return m_resource->getPath(); }
const char* getName() const override { return "pipeline editor"; }
StudioApp& m_app;
PipelineResource* m_resource;
UniquePtr<CodeEditor> m_editor;
};
explicit PipelinePlugin(StudioApp& app)
: m_app(app)
{}
bool compile(const Path& src) override { return m_app.getAssetCompiler().copyCompile(src); }
const char* getLabel() const override { return "Pipeline"; }
void openEditor(const struct Path& path) override {
IAllocator& allocator = m_app.getAllocator();
UniquePtr<EditorWindow> win = UniquePtr<EditorWindow>::create(allocator, path, m_app, m_app.getAllocator());
m_app.getAssetBrowser().addWindow(win.move());
}
StudioApp& m_app;
};
struct ParticleSystemPropertyPlugin final : PropertyGrid::IPlugin
{
ParticleSystemPropertyPlugin(StudioApp& app) : m_app(app) {}
@ -2707,7 +2634,7 @@ struct ModelPlugin final : AssetBrowser::IPlugin, AssetCompiler::IPlugin {
{
Engine& engine = m_app.getEngine();
m_tile.world = &engine.createWorld(false);
PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
LuaScript* pres = engine.getResourceManager().load<LuaScript>(Path("pipelines/main.lua"));
m_tile.pipeline = Pipeline::create(*m_renderer, pres, "PREVIEW");
RenderModule* render_module = (RenderModule*)m_tile.world->getModule(MODEL_INSTANCE_TYPE);
@ -3525,7 +3452,7 @@ struct EnvironmentProbePlugin final : PropertyGrid::IPlugin
SystemManager& system_manager = engine.getSystemManager();
Renderer* renderer = static_cast<Renderer*>(system_manager.getSystem("renderer"));
ResourceManagerHub& rm = engine.getResourceManager();
PipelineResource* pres = rm.load<PipelineResource>(Path("pipelines/main.pln"));
LuaScript* pres = rm.load<LuaScript>(Path("pipelines/main.lua"));
m_pipeline = Pipeline::create(*renderer, pres, "PROBE");
m_ibl_filter_shader = rm.load<Shader>(Path("pipelines/ibl_filter.shd"));
}
@ -4110,10 +4037,8 @@ struct InstancedModelPlugin final : PropertyGrid::IPlugin, StudioApp::MousePlugi
if (!cmp.im->model || !cmp.im->model->isReady()) return false;
WorldEditor& editor = m_app.getWorldEditor();
DVec3 ray_origin;
Vec3 ray_dir;
editor.getView().getViewport().getRay(Vec2((float)x, (float)y), ray_origin, ray_dir);
const RayCastModelHit hit = m_brush != Brush::TERRAIN ? cmp.module->castRay(ray_origin, ray_dir, INVALID_ENTITY) : cmp.module->castRayTerrain(ray_origin, ray_dir);
const Ray ray = editor.getView().getViewport().getRay(Vec2((float)x, (float)y));
const RayCastModelHit hit = m_brush != Brush::TERRAIN ? cmp.module->castRay(ray, INVALID_ENTITY) : cmp.module->castRayTerrain(ray);
if (!hit.is_hit) return false;
const DVec3 hit_pos = hit.origin + hit.t * hit.dir;
@ -4231,10 +4156,8 @@ struct InstancedModelPlugin final : PropertyGrid::IPlugin, StudioApp::MousePlugi
if (!cmp.im) return false;
if (!cmp.im->model || !cmp.im->model->isReady()) return false;
DVec3 ray_origin;
Vec3 ray_dir;
view.getViewport().getRay(Vec2((float)x, (float)y), ray_origin, ray_dir);
RayCastModelHit hit = cmp.module->castRayInstancedModels(ray_origin, ray_dir, [](const RayCastModelHit&){ return true; });
const Ray ray = view.getViewport().getRay(Vec2((float)x, (float)y));
RayCastModelHit hit = cmp.module->castRayInstancedModels(ray, [](const RayCastModelHit&){ return true; });
if (hit.is_hit && hit.entity == cmp.entity) {
m_selected = cmp.module->getInstancedModels()[cmp.entity].instances[hit.subindex];
return true;
@ -4389,10 +4312,8 @@ struct InstancedModelPlugin final : PropertyGrid::IPlugin, StudioApp::MousePlugi
if (ImGui::GetIO().KeyShift) {
const Vec2 mp = editor.getView().getMousePos();
DVec3 ray_origin;
Vec3 ray_dir;
editor.getView().getViewport().getRay(mp, ray_origin, ray_dir);
const RayCastModelHit hit = render_module->castRayTerrain(ray_origin, ray_dir);
const Ray ray = editor.getView().getViewport().getRay(mp);
const RayCastModelHit hit = render_module->castRayTerrain(ray);
if (hit.is_hit) {
drawCircle(*render_module, hit.origin + hit.t * hit.dir, m_brush_radius, 0xff880000);
}
@ -4485,10 +4406,8 @@ struct ProceduralGeomPlugin final : PropertyGrid::IPlugin, StudioApp::MousePlugi
RenderModule* module = (RenderModule*)world.getModule("renderer");
if (!world.hasComponent(entity, PROCEDURAL_GEOM_TYPE)) return false;
DVec3 origin;
Vec3 dir;
view.getViewport().getRay({(float)x, (float)y}, origin, dir);
const RayCastModelHit hit = module->castRay(origin, dir, [entity](const RayCastModelHit& hit) {
const Ray ray = view.getViewport().getRay({(float)x, (float)y});
const RayCastModelHit hit = module->castRay(ray, [entity](const RayCastModelHit& hit) {
return hit.entity == entity;
});
if (!hit.is_hit) return false;
@ -4510,10 +4429,8 @@ struct ProceduralGeomPlugin final : PropertyGrid::IPlugin, StudioApp::MousePlugi
World& world = *editor.getWorld();
RenderModule* module = static_cast<RenderModule*>(world.getModule("renderer"));
DVec3 origin;
Vec3 dir;
view.getViewport().getRay(mp, origin, dir);
const RayCastModelHit hit = module->castRay(origin, dir, [entity](const RayCastModelHit& hit){
const Ray ray = view.getViewport().getRay(mp);
const RayCastModelHit hit = module->castRay(ray, [entity](const RayCastModelHit& hit){
return hit.entity == entity;
});
@ -4824,10 +4741,10 @@ struct RenderInterfaceImpl final : RenderInterface
}
WorldView::RayHit castRay(World& world, const DVec3& origin, const Vec3& dir, EntityPtr ignored) override
WorldView::RayHit castRay(World& world, const Ray& ray, EntityPtr ignored) override
{
RenderModule* module = (RenderModule*)world.getModule(ENVIRONMENT_PROBE_TYPE);
const RayCastModelHit hit = module->castRay(origin, dir, ignored);
const RayCastModelHit hit = module->castRay(ray, ignored);
return {hit.is_hit, hit.t, hit.entity, hit.origin + hit.dir * hit.t};
}
@ -5233,7 +5150,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
{
StudioAppPlugin(StudioApp& app)
: m_app(app)
, m_pipeline_plugin(app)
, m_font_plugin(app)
, m_material_plugin(app)
, m_particle_emitter_property_plugin(app)
@ -5293,9 +5209,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
const char* texture_exts[] = {"png", "jpg", "jpeg", "tga", "raw", "ltc"};
asset_compiler.addPlugin(m_texture_plugin, Span(texture_exts));
const char* pipeline_exts[] = {"pln"};
asset_compiler.addPlugin(m_pipeline_plugin, Span(pipeline_exts));
const char* material_exts[] = {"mat"};
asset_compiler.addPlugin(m_material_plugin, Span(material_exts));
@ -5313,7 +5226,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
asset_browser.addPlugin(m_shader_plugin, Span(shader_exts));
asset_browser.addPlugin(m_shader_include_plugin, Span(inc_exts));
asset_browser.addPlugin(m_texture_plugin, Span(texture_exts));
asset_browser.addPlugin(m_pipeline_plugin, Span(pipeline_exts));
m_app.addPlugin(m_scene_view);
m_app.addPlugin(m_game_view);
@ -5532,7 +5444,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
asset_browser.removePlugin(m_texture_plugin);
asset_browser.removePlugin(m_shader_plugin);
asset_browser.removePlugin(m_shader_include_plugin);
asset_browser.removePlugin(m_pipeline_plugin);
AssetCompiler& asset_compiler = m_app.getAssetCompiler();
asset_compiler.removePlugin(m_font_plugin);
@ -5541,7 +5452,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
asset_compiler.removePlugin(m_texture_plugin);
asset_compiler.removePlugin(m_model_plugin);
asset_compiler.removePlugin(m_material_plugin);
asset_compiler.removePlugin(m_pipeline_plugin);
m_app.removePlugin(m_scene_view);
m_app.removePlugin(m_game_view);
@ -5565,7 +5475,6 @@ struct StudioAppPlugin : StudioApp::IPlugin
EditorUIRenderPlugin m_editor_ui_render_plugin;
MaterialPlugin m_material_plugin;
ParticleSystemPropertyPlugin m_particle_emitter_property_plugin;
PipelinePlugin m_pipeline_plugin;
FontPlugin m_font_plugin;
ShaderIncludePlugin m_shader_include_plugin;
ShaderPlugin m_shader_plugin;

View File

@ -24,6 +24,7 @@
#include "engine/resource_manager.h"
#include "engine/string.h"
#include "engine/world.h"
#include "lua_script/lua_script.h"
#include "renderer/culling_system.h"
#include "renderer/draw2d.h"
#include "renderer/font.h"
@ -122,10 +123,8 @@ struct WorldViewImpl final : WorldView {
{
if (m_snap_mode != SnapMode::VERTEX) return;
DVec3 origin;
Vec3 dir;
m_viewport.getRay(m_mouse_pos, origin, dir);
const RayCastModelHit hit = m_module->castRay(origin, dir, INVALID_ENTITY);
const Ray ray = m_viewport.getRay(m_mouse_pos);
const RayCastModelHit hit = m_module->castRay(ray, INVALID_ENTITY);
if (!hit.is_hit) return;
const DVec3 snap_pos = getClosestVertex(hit);
@ -183,15 +182,13 @@ struct WorldViewImpl final : WorldView {
}
else
{
DVec3 origin;
Vec3 dir;
m_viewport.getRay(m_mouse_pos, origin, dir);
const RayCastModelHit hit = m_module->castRay(origin, dir, INVALID_ENTITY);
const Ray ray = m_viewport.getRay(m_mouse_pos);
const RayCastModelHit hit = m_module->castRay(ray, INVALID_ENTITY);
const Array<EntityRef>& selected_entities = m_editor.getSelectedEntities();
if (m_snap_mode != SnapMode::NONE && !selected_entities.empty() && hit.is_hit)
{
DVec3 snap_pos = origin + dir * hit.t;
DVec3 snap_pos = ray.origin + ray.dir * hit.t;
if (m_snap_mode == SnapMode::VERTEX) snap_pos = getClosestVertex(hit);
const Quat rot = m_editor.getWorld()->getRotation(selected_entities[0]);
const Gizmo::Config& gizmo_cfg = m_app.getGizmoConfig();
@ -200,7 +197,7 @@ struct WorldViewImpl final : WorldView {
}
else
{
auto icon_hit = m_icons->raycast(origin, dir);
auto icon_hit = m_icons->raycast(ray.origin, ray.dir);
if (icon_hit.entity != INVALID_ENTITY) {
if(icon_hit.entity.isValid()) {
EntityRef e = (EntityRef)icon_hit.entity;
@ -239,10 +236,8 @@ struct WorldViewImpl final : WorldView {
const Array<EntityRef>& selected_entities = m_editor.getSelectedEntities();
if (selected_entities.empty()) return;
DVec3 origin;
Vec3 dir;
m_viewport.getRay(m_mouse_pos, origin, dir);
const RayCastModelHit hit = m_module->castRay(origin, dir, INVALID_ENTITY);
const Ray ray = m_viewport.getRay(m_mouse_pos);
const RayCastModelHit hit = m_module->castRay(ray, INVALID_ENTITY);
if (!hit.is_hit || hit.entity != selected_entities[0]) return;
const DVec3 snap_pos = getClosestVertex(hit);
@ -351,10 +346,8 @@ struct WorldViewImpl final : WorldView {
}
else if (button == os::MouseButton::LEFT)
{
DVec3 origin;
Vec3 dir;
m_viewport.getRay({(float)x, (float)y}, origin, dir);
const RayCastModelHit hit = m_module->castRay(origin, dir, INVALID_ENTITY);
const Ray ray = m_viewport.getRay({(float)x, (float)y});
const RayCastModelHit hit = m_module->castRay(ray, INVALID_ENTITY);
if (Gizmo::isActive()) return;
if (m_scene_view.m_is_measure_active) {
@ -537,13 +530,11 @@ struct WorldViewImpl final : WorldView {
RayHit res;
const Vec2 center{float(cam_x), float(cam_y)};
DVec3 origin;
Vec3 dir;
m_viewport.getRay(center, origin, dir);
const RayCastModelHit hit = m_module->castRay(origin, dir, ignore);
const Ray ray = m_viewport.getRay(center);
const RayCastModelHit hit = m_module->castRay(ray, ignore);
DVec3 pos;
if (hit.is_hit) {
res.pos = origin + dir * hit.t;
res.pos = ray.origin + ray.dir * hit.t;
res.t = hit.t;
res.entity = hit.entity;
res.is_hit = true;
@ -675,10 +666,6 @@ SceneView::SceneView(StudioApp& app)
m_app.addAction(&m_move_entity_S_action);
m_app.addAction(&m_move_entity_W_action);
m_app.addAction(&m_toggle_projection_action);
const ResourceType pipeline_type("pipeline");
m_app.getAssetCompiler().registerExtension("pln", pipeline_type);
}
void SceneView::toggleProjection() {
@ -693,7 +680,7 @@ void SceneView::init() {
Engine& engine = m_app.getEngine();
auto* renderer = static_cast<Renderer*>(engine.getSystemManager().getSystem("renderer"));
PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
LuaScript* pres = engine.getResourceManager().load<LuaScript>(Path("pipelines/main.lua"));
m_pipeline = Pipeline::create(*renderer, pres, "SCENE_VIEW");
m_pipeline->addCustomCommandHandler("renderSelection").callback.bind<&SceneView::renderSelection>(this);
m_pipeline->addCustomCommandHandler("renderGizmos").callback.bind<&SceneView::renderGizmos>(this);
@ -1098,10 +1085,8 @@ RayCastModelHit SceneView::castRay(float x, float y)
ASSERT(module);
const Viewport& vp = m_view->getViewport();
DVec3 origin;
Vec3 dir;
vp.getRay({x * vp.w, y * vp.h}, origin, dir);
return module->castRay(origin, dir, INVALID_ENTITY);
const Ray ray = vp.getRay({x * vp.w, y * vp.h});
return module->castRay(ray, INVALID_ENTITY);
}

View File

@ -915,10 +915,8 @@ bool TerrainEditor::onMouseDown(WorldView& view, int x, int y)
if (!is_terrain) return false;
RenderModule* module = (RenderModule*)world.getModule(TERRAIN_TYPE);
DVec3 origin;
Vec3 dir;
view.getViewport().getRay({(float)x, (float)y}, origin, dir);
const RayCastModelHit hit = module->castRayTerrain(origin, dir);
const Ray ray = view.getViewport().getRay({(float)x, (float)y});
const RayCastModelHit hit = module->castRayTerrain(ray);
if (!hit.is_hit) return false;
const DVec3 hit_pos = hit.origin + hit.dir * hit.t;
@ -1184,11 +1182,9 @@ void TerrainEditor::onMouseMove(WorldView& view, int x, int y, int, int)
World& world = *editor.getWorld();
if (!world.hasComponent(entity, TERRAIN_TYPE)) return;
RenderModule* module = (RenderModule*)world.getModule(TERRAIN_TYPE);
DVec3 origin;
Vec3 dir;
view.getViewport().getRay({(float)x, (float)y}, origin, dir);
const Ray ray = view.getViewport().getRay({(float)x, (float)y});
const RayCastModelHit hit = module->castRayTerrain(origin, dir);
const RayCastModelHit hit = module->castRayTerrain(ray);
if (!hit.is_hit) return;
if (hit.entity != entity) return;
@ -2038,10 +2034,8 @@ void TerrainEditor::onGUI(ComponentUID cmp, WorldEditor& editor) {
for(auto entity : editor.getSelectedEntities()) {
if (!world.hasComponent(entity, TERRAIN_TYPE)) continue;
DVec3 origin;
Vec3 dir;
editor.getView().getViewport().getRay(mp, origin, dir);
const RayCastModelHit hit = module->castRayTerrain(origin, dir);
const Ray ray = editor.getView().getViewport().getRay(mp);
const RayCastModelHit hit = module->castRayTerrain(ray);
if(hit.is_hit) {
DVec3 center = hit.origin + hit.dir * hit.t;

View File

@ -6,6 +6,7 @@
#include "editor/studio_app.h"
#include "editor/settings.h"
#include "editor/utils.h"
#include "lua_script/lua_script.h"
#include "renderer/model.h"
#include "renderer/pose.h"
#include "renderer/render_module.h"
@ -31,7 +32,7 @@ WorldViewer::WorldViewer(StudioApp& app)
m_viewport.rot = Quat::IDENTITY;
m_world = &engine.createWorld(false);
PipelineResource* pres = engine.getResourceManager().load<PipelineResource>(Path("pipelines/main.pln"));
LuaScript* pres = engine.getResourceManager().load<LuaScript>(Path("pipelines/main.lua"));
m_pipeline = Pipeline::create(*renderer, pres, "PREVIEW");
const EntityRef mesh_entity = m_world->createEntity({0, 0, 0}, {0, 0, 0, 1});

View File

@ -19,6 +19,7 @@
#include "engine/resource_manager.h"
#include "engine/stack_array.h"
#include "engine/world.h"
#include "lua_script/lua_script.h"
#include "culling_system.h"
#include "draw2d.h"
#include "draw_stream.h"
@ -356,31 +357,9 @@ struct ShadowAtlas {
EntityPtr inv_map[64];
};
static const float SHADOW_CAM_FAR = 500.0f;
ResourceType PipelineResource::TYPE("pipeline");
void PipelineResource::unload()
{
content.resize(0);
}
bool PipelineResource::load(Span<const u8> mem) {
content = StringView((const char*)mem.begin(), mem.length());
return true;
}
PipelineResource::PipelineResource(const Path& path, ResourceManager& owner, Renderer&, IAllocator& allocator)
: Resource(path, owner, allocator)
, content(allocator)
{}
struct PipelineImpl final : Pipeline
{
using RenderbufferDescHandle = u32;
@ -689,7 +668,7 @@ struct PipelineImpl final : Pipeline
}
PipelineImpl(Renderer& renderer, PipelineResource* resource, const char* define, IAllocator& allocator)
PipelineImpl(Renderer& renderer, LuaScript* resource, const char* define, IAllocator& allocator)
: m_allocator(allocator)
, m_renderer(renderer)
, m_resource(resource)
@ -931,10 +910,8 @@ struct PipelineImpl final : Pipeline
setDefine();
const char* content = m_resource->content.c_str();
const int content_size = m_resource->content.length();
bool errors =
LuaWrapper::luaL_loadbuffer(m_lua_state, content, content_size, m_resource->getPath().c_str()) != 0;
StringView content = m_resource->getSourceCode();
bool errors = LuaWrapper::luaL_loadbuffer(m_lua_state, content.begin, content.size(), m_resource->getPath().c_str()) != 0;
if (errors)
{
logError(m_resource->getPath(), ": ", lua_tostring(m_lua_state, -1));
@ -3075,6 +3052,7 @@ struct PipelineImpl final : Pipeline
void fillClusters(DrawStream& stream, const CameraParams& cp) {
PROFILE_FUNCTION();
ASSERT(cp.frustum.xs[0] == cp.frustum.xs[0]);
ASSERT(cp.pos.x == cp.pos.x);
struct ClusterLight {
Vec3 pos;
@ -4076,7 +4054,7 @@ struct PipelineImpl final : Pipeline
IAllocator& m_allocator;
Renderer& m_renderer;
PipelineResource* m_resource;
LuaScript* m_resource;
lua_State* m_lua_state;
int m_lua_thread_ref;
int m_lua_env;
@ -4132,7 +4110,7 @@ struct PipelineImpl final : Pipeline
};
UniquePtr<Pipeline> Pipeline::create(Renderer& renderer, PipelineResource* resource, const char* define)
UniquePtr<Pipeline> Pipeline::create(Renderer& renderer, LuaScript* resource, const char* define)
{
return UniquePtr<PipelineImpl>::create(renderer.getAllocator(), renderer, resource, define, renderer.getAllocator());
}

View File

@ -44,18 +44,6 @@ namespace UniformBuffer {
};
}
struct LUMIX_RENDERER_API PipelineResource : Resource {
static ResourceType TYPE;
PipelineResource(const Path& path, ResourceManager& owner, Renderer& renderer, IAllocator& allocator);
void unload() override;
bool load(Span<const u8> mem) override;
ResourceType getType() const override { return TYPE; }
String content;
};
struct LUMIX_RENDERER_API Pipeline {
struct CustomCommandHandler {
@ -64,7 +52,7 @@ struct LUMIX_RENDERER_API Pipeline {
RuntimeHash hash;
};
static UniquePtr<Pipeline> create(Renderer& renderer, PipelineResource* resource, const char* define);
static UniquePtr<Pipeline> create(Renderer& renderer, struct LuaScript* resource, const char* define);
virtual ~Pipeline() {}

View File

@ -2026,7 +2026,7 @@ struct RenderModuleImpl final : RenderModule {
const Ray ray = module->getCameraRay(camera_entity, {x, y});
RayCastModelHit hit = module->castRay(ray.origin, ray.dir, INVALID_ENTITY);
RayCastModelHit hit = module->castRay(ray, INVALID_ENTITY);
LuaWrapper::push(L, hit.is_hit);
LuaWrapper::push(L, hit.is_hit ? hit.origin + hit.dir * hit.t : DVec3(0));
LuaWrapper::pushEntity(L, hit.is_hit ? hit.entity : INVALID_ENTITY, &module->getWorld());
@ -2518,12 +2518,12 @@ struct RenderModuleImpl final : RenderModule {
}
RayCastModelHit castRayTerrain(const DVec3& origin, const Vec3& dir) override
RayCastModelHit castRayTerrain(const Ray& ray) override
{
RayCastModelHit hit;
hit.is_hit = false;
for (Terrain* terrain : m_terrains) {
hit = terrain->castRay(origin, dir);
hit = terrain->castRay(ray);
hit.component_type = TERRAIN_TYPE;
hit.entity = terrain->getEntity();
if (hit.is_hit) break;
@ -2531,13 +2531,13 @@ struct RenderModuleImpl final : RenderModule {
return hit;
}
RayCastModelHit castRay(const DVec3& origin, const Vec3& dir, EntityPtr ignored_model_instance) override {
return castRay(origin, dir, [&](const RayCastModelHit& hit) -> bool {
RayCastModelHit castRay(const Ray& ray, EntityPtr ignored_model_instance) override {
return castRay(ray, [&](const RayCastModelHit& hit) -> bool {
return hit.entity != ignored_model_instance || !ignored_model_instance.isValid();
});
}
RayCastModelHit castRayInstancedModels(const DVec3& ray_origin, const Vec3& ray_dir, const RayCastModelHit::Filter& filter) override {
RayCastModelHit castRayInstancedModels(const Ray& ray, const RayCastModelHit::Filter& filter) override {
RayCastModelHit hit;
hit.is_hit = false;
for (auto iter = m_instanced_models.begin(), end = m_instanced_models.end(); iter != end; ++iter) {
@ -2556,12 +2556,12 @@ struct RenderModuleImpl final : RenderModule {
return res;
};
for (const InstancedModel::InstanceData& id : im.instances) {
Vec3 rel_pos = Vec3(ray_origin - tr.pos) - id.pos;
Vec3 rel_pos = Vec3(ray.origin - tr.pos) - id.pos;
const float radius = model_radius * id.scale;
float intersection_t;
if (getRaySphereIntersection(rel_pos, ray_dir, Vec3::ZERO, radius, intersection_t) && intersection_t >= 0) {
if (getRaySphereIntersection(rel_pos, ray.dir, Vec3::ZERO, radius, intersection_t) && intersection_t >= 0) {
const Quat rot = getInstanceQuat(id.rot_quat);
const Vec3 rel_dir = rot.conjugated().rotate(ray_dir);
const Vec3 rel_dir = rot.conjugated().rotate(ray.dir);
rel_pos = rot.conjugated().rotate(rel_pos / id.scale);
RayCastModelHit new_hit = im.model->castRay(rel_pos, rel_dir, nullptr, e, &filter);
if (new_hit.is_hit && (!hit.is_hit || new_hit.t * id.scale < hit.t)) {
@ -2578,7 +2578,7 @@ struct RenderModuleImpl final : RenderModule {
return hit;
}
RayCastModelHit castRayProceduralGeometry(const DVec3& origin, const Vec3& dir, const RayCastModelHit::Filter& filter) {
RayCastModelHit castRayProceduralGeometry(const Ray& ray, const RayCastModelHit::Filter& filter) {
RayCastModelHit hit;
hit.is_hit = false;
for (auto iter = m_procedural_geometries.begin(), end = m_procedural_geometries.end(); iter != end; ++iter) {
@ -2593,8 +2593,8 @@ struct RenderModuleImpl final : RenderModule {
const DVec3& pos = m_world.getPosition(iter.key());
const Quat rot = m_world.getRotation(iter.key()).conjugated();
const Vec3 rd = rot.rotate(dir);
Vec3 ro = Vec3(origin - pos);
const Vec3 rd = rot.rotate(ray.dir);
Vec3 ro = Vec3(ray.origin - pos);
Vec3 dummy;
if (!pg.aabb.contains(ro) && !getRayAABBIntersection(ro, rd, pg.aabb.min, pg.aabb.max - pg.aabb.min, dummy)) continue;
@ -2637,17 +2637,16 @@ struct RenderModuleImpl final : RenderModule {
}
}
}
hit.origin = origin;
hit.dir = dir;
hit.origin = ray.origin;
hit.dir = ray.dir;
return hit;
}
RayCastModelHit castRay(const DVec3& origin, const Vec3& dir, const Delegate<bool (const RayCastModelHit&)> filter) override {
RayCastModelHit castRay(const Ray& ray, const Delegate<bool (const RayCastModelHit&)> filter) override {
PROFILE_FUNCTION();
ASSERT(length(dir) > 0.99f && length(dir) < 1.01f);
RayCastModelHit hit = castRayInstancedModels(origin, dir, filter);
RayCastModelHit hit = castRayInstancedModels(ray, filter);
double cur_dist = hit.is_hit ? hit.t : DBL_MAX;
const World& world = getWorld();
@ -2659,12 +2658,12 @@ struct RenderModuleImpl final : RenderModule {
const EntityRef entity{i};
const Transform& tr = world.getTransform(entity);
float radius = r.model->getOriginBoundingRadius();
const double dist = length(tr.pos - origin);
const double dist = length(tr.pos - ray.origin);
if (dist - radius * maximum(tr.scale.x, tr.scale.y, tr.scale.z) > cur_dist) continue;
const Transform& inv_tr = tr.inverted();
const Vec3 ray_origin_model_space = Vec3(inv_tr.transform(origin));
const Vec3 ray_dir_model_space = normalize(inv_tr.transformVector(dir));
const Vec3 ray_origin_model_space = Vec3(inv_tr.transform(ray.origin));
const Vec3 ray_dir_model_space = normalize(inv_tr.transformVector(ray.dir));
float intersection_t;
if (getRaySphereIntersection(ray_origin_model_space, ray_dir_model_space, Vec3::ZERO, radius, intersection_t) && intersection_t >= 0) {
@ -2675,13 +2674,13 @@ struct RenderModuleImpl final : RenderModule {
if (new_hit.is_hit) {
const Vec3 hit_pos_model_space = Vec3(new_hit.origin + new_hit.dir * new_hit.t);
const DVec3 new_hit_pos = tr.transform(hit_pos_model_space);
const float new_t = (float)length(origin - new_hit_pos);
const float new_t = (float)length(ray.origin - new_hit_pos);
if (!hit.is_hit || new_t < hit.t) {
new_hit.entity = entity;
new_hit.component_type = MODEL_INSTANCE_TYPE;
hit = new_hit;
hit.origin = origin;
hit.dir = dir;
hit.origin = ray.origin;
hit.dir = ray.dir;
hit.t = new_t;
hit.is_hit = true;
cur_dist = hit.t;
@ -2691,14 +2690,14 @@ struct RenderModuleImpl final : RenderModule {
}
}
const RayCastModelHit pg_hit = castRayProceduralGeometry(origin, dir, filter);
const RayCastModelHit pg_hit = castRayProceduralGeometry(ray, filter);
if (pg_hit.is_hit && (pg_hit.t < hit.t || !hit.is_hit)) {
hit = pg_hit;
hit.component_type = PROCEDURAL_GEOM_TYPE;
}
for (auto* terrain : m_terrains) {
RayCastModelHit terrain_hit = terrain->castRay(origin, dir);
RayCastModelHit terrain_hit = terrain->castRay(ray);
if (terrain_hit.is_hit && (!hit.is_hit || terrain_hit.t < hit.t)) {
terrain_hit.component_type = TERRAIN_TYPE;
terrain_hit.entity = terrain->getEntity();
@ -2707,8 +2706,8 @@ struct RenderModuleImpl final : RenderModule {
}
}
hit.origin = origin;
hit.dir = dir;
hit.origin = ray.origin;
hit.dir = ray.dir;
return hit;
}
@ -3360,10 +3359,20 @@ void RenderModule::reflect() {
}
};
reflection::structure<Ray>("Ray")
.member<&Ray::origin>("origin")
.member<&Ray::dir>("dir");
reflection::structure<RayCastModelHit>("RayCastModelHit")
.member<&RayCastModelHit::is_hit>("is_hit")
.member<&RayCastModelHit::t>("t")
.member<&RayCastModelHit::entity>("entity");
LUMIX_MODULE(RenderModuleImpl, "renderer")
.LUMIX_FUNC(addDebugCross)
.LUMIX_FUNC(addDebugLine)
.LUMIX_FUNC(addDebugTriangle)
.function<(RayCastModelHit (RenderModuleImpl::*)(const Ray&, EntityPtr))&RenderModuleImpl::castRay>("castRay", "RenderModuleImpl::castRay")
.LUMIX_FUNC(setActiveCamera)
.LUMIX_CMP(ProceduralGeometry, "procedural_geom", "Render / Procedural geometry")
.LUMIX_PROP(ProceduralGeometryMaterial, "Material").resourceAttribute(Material::TYPE)
@ -3391,6 +3400,7 @@ void RenderModule::reflect() {
.LUMIX_PROP(ParticleSystemPath, "Source").resourceAttribute(ParticleSystemResource::TYPE)
.LUMIX_CMP(Camera, "camera", "Render / Camera")
.icon(ICON_FA_CAMERA)
.function<&RenderModule::getCameraRay>("getRay", "getCameraRay")
.var_prop<&RenderModule::getCamera, &Camera::fov>("FOV").radiansAttribute()
.var_prop<&RenderModule::getCamera, &Camera::near>("Near").minAttribute(0)
.var_prop<&RenderModule::getCamera, &Camera::far>("Far").minAttribute(0)

View File

@ -280,6 +280,7 @@ enum class RenderModuleVersion : i32 {
LATEST
};
struct LUMIX_RENDERER_API RenderModule : IModule
{
static UniquePtr<RenderModule> createInstance(Renderer& renderer,
@ -289,10 +290,10 @@ struct LUMIX_RENDERER_API RenderModule : IModule
static void registerLuaAPI(lua_State* L, Renderer& renderer);
static void reflect();
virtual RayCastModelHit castRay(const DVec3& origin, const Vec3& dir, const Delegate<bool (const RayCastModelHit&)> filter) = 0;
virtual RayCastModelHit castRay(const DVec3& origin, const Vec3& dir, EntityPtr ignore) = 0;
virtual RayCastModelHit castRayTerrain(const DVec3& origin, const Vec3& dir) = 0;
virtual RayCastModelHit castRayInstancedModels(const DVec3& ray_origin, const Vec3& ray_dir, const Delegate<bool (const RayCastModelHit&)>& filter) = 0;
virtual RayCastModelHit castRay(const Ray& ray, const Delegate<bool (const RayCastModelHit&)> filter) = 0;
virtual RayCastModelHit castRay(const Ray& ray, EntityPtr ignore) = 0;
virtual RayCastModelHit castRayTerrain(const Ray& ray) = 0;
virtual RayCastModelHit castRayInstancedModels(const Ray& ray, const Delegate<bool (const RayCastModelHit&)>& filter) = 0;
virtual struct Ray getCameraRay(EntityRef entity, const Vec2& screen_pos) = 0;
virtual void setActiveCamera(EntityRef camera) = 0;

View File

@ -368,7 +368,6 @@ struct RendererImpl final : Renderer
: m_engine(engine)
, m_allocator(engine.getAllocator(), "renderer")
, m_texture_manager("textures", *this, m_allocator)
, m_pipeline_manager("pipelines", *this, m_allocator)
, m_model_manager("models", *this, m_allocator)
, m_particle_emitter_manager("particle emitters", *this, m_allocator)
, m_material_manager(*this, m_allocator)
@ -404,7 +403,6 @@ struct RendererImpl final : Renderer
~RendererImpl()
{
m_particle_emitter_manager.destroy();
m_pipeline_manager.destroy();
m_texture_manager.destroy();
m_model_manager.destroy();
m_material_manager.destroy();
@ -557,7 +555,6 @@ struct RendererImpl final : Renderer
stream.update(mb.buffer, &default_mat, sizeof(default_mat));
ResourceManagerHub& manager = m_engine.getResourceManager();
m_pipeline_manager.create(PipelineResource::TYPE, manager);
m_texture_manager.create(Texture::TYPE, manager);
m_model_manager.create(Model::TYPE, manager);
m_material_manager.create(Material::TYPE, manager);
@ -1003,7 +1000,6 @@ struct RendererImpl final : Renderer
MaterialManager m_material_manager;
RenderResourceManager<Model> m_model_manager;
RenderResourceManager<ParticleSystemResource> m_particle_emitter_manager;
RenderResourceManager<PipelineResource> m_pipeline_manager;
RenderResourceManager<Shader> m_shader_manager;
RenderResourceManager<Texture> m_texture_manager;
Array<u32> m_free_sort_keys;

View File

@ -475,7 +475,7 @@ void Terrain::setHeight(int x, int z, float h)
}
RayCastModelHit Terrain::castRay(const DVec3& origin, const Vec3& dir)
RayCastModelHit Terrain::castRay(const Ray& ray)
{
RayCastModelHit hit;
hit.is_hit = false;
@ -485,8 +485,8 @@ RayCastModelHit Terrain::castRay(const DVec3& origin, const Vec3& dir)
const World& world = m_module.getWorld();
const Quat rot = world.getRotation(m_entity);
const DVec3 pos = world.getPosition(m_entity);
const Vec3 rel_dir = rot.rotate(dir);
const Vec3 terrain_to_ray = Vec3(origin - pos);
const Vec3 rel_dir = rot.rotate(ray.dir);
const Vec3 terrain_to_ray = Vec3(ray.origin - pos);
const Vec3 rel_origin = rot.conjugated().rotate(terrain_to_ray);
Vec3 start;
@ -514,15 +514,15 @@ RayCastModelHit Terrain::castRay(const DVec3& origin, const Vec3& dir)
Vec3 p3(x, getHeight(x, z + m_scale.x), z + m_scale.x);
if (getRayTriangleIntersection(rel_origin, rel_dir, p0, p1, p2, &t)) {
hit.is_hit = true;
hit.origin = origin;
hit.dir = dir;
hit.origin = ray.origin;
hit.dir = ray.dir;
hit.t = t;
return hit;
}
if (getRayTriangleIntersection(rel_origin, rel_dir, p0, p2, p3, &t)) {
hit.is_hit = true;
hit.origin = origin;
hit.dir = dir;
hit.origin = ray.origin;
hit.dir = ray.dir;
hit.t = t;
return hit;
}

View File

@ -99,7 +99,7 @@ struct Terrain {
void setGrassTypeRotationMode(int index, GrassType::RotationMode mode);
void setMaterial(Material* material);
RayCastModelHit castRay(const DVec3& origin, const Vec3& dir);
RayCastModelHit castRay(const Ray& ray);
void serialize(OutputMemoryStream& serializer);
void deserialize(EntityRef entity, InputMemoryStream& serializer, World& world, RenderModule& module, i32 version);