physics - sweep lua API; code editor improvements

This commit is contained in:
Mikulas Florek 2023-10-05 13:19:50 +02:00
parent 958098cfc0
commit 24914869ae
12 changed files with 618 additions and 138 deletions

View File

@ -1,4 +1,9 @@
local tpl = [[
export type Vec2 = {number}
export type Vec3 = {number}
export type Color = {number}
export type Quat = {number}
export type DVec3 = {number}
declare ImGui: {
AlignTextToFramePadding : () -> (),
Begin : (string, boolean?) -> (boolean, boolean?),
@ -82,8 +87,8 @@ declare class Entity
name : string
parent : Entity?
rotation : any
position : any
scale : any
position : Vec3
scale : Vec3
hasComponent : (Entity, any) -> boolean
getComponent : (Entity, any) -> any
destroy : (Entity) -> ()
@ -115,6 +120,7 @@ declare Editor: {
}
declare LumixAPI: {
%s
INPUT_KEYCODE_SHIFT: number,
INPUT_KEYCODE_LEFT : number,
INPUT_KEYCODE_RIGHT : number,
@ -133,6 +139,12 @@ end
declare class FunctionBase
end
declare class StructVarBase
end
declare class StructBase
end
declare class ModuleReflection
end
@ -159,6 +171,13 @@ declare LumixReflection: {
getFunction : (number) -> FunctionBase,
getThisTypeName : (FunctionBase) -> string,
getReturnTypeName : (FunctionBase) -> string,
getNumStructs : () -> number,
getStruct : (number) -> StructBase,
getStructName : (StructBase) -> string,
getNumStructMembers : (StructBase) -> number,
getStructMember : (StructBase, number) -> StructVarBase,
getStructMemberName : (StructVarBase) -> string,
getStructMemberType : (StructVarBase) -> number
}
type InputDevice = {
@ -193,6 +212,7 @@ export type InputEvent = ButtonInputEvent | AxisInputEvent
function typeToString(type : number) : string
if type < 3 then return "number" end
if type == 3 then return "Entity" end
if type == 5 then return "Vec3" end
if type == 8 then return "string" end
if type == 9 then return "boolean" end
if type == 10 then return "string" end
@ -224,9 +244,21 @@ export type InputEvent = ButtonInputEvent | AxisInputEvent
end
function toLuaType(ctype : string)
if string.match(ctype, "^struct Lumix::") then
ctype = string.sub(ctype, 15)
end
if ctype == "int" then return "number" end
if ctype == "const char *" then return "string" end
if ctype == "const char*" then return "string" end
if ctype == "char const *" then return "string" end
if ctype == "Vec3" then return "Vec3" end
if ctype == "Quat" then return "Quat" end
if ctype == "Vec2" then return "Vec2" end
if ctype == "Color" then return "Color" end
if ctype == "DVec3" then return "DVec3" end
if ctype == "EntityPtr" then return "Entity?" end
if ctype == "EntityRef" then return "Entity" end
if ctype == "Path" then return "string" end
if ctype == "i32" then return "number" end
if ctype == "u32" then return "number" end
if ctype == "float" then return "number" end
@ -237,8 +269,36 @@ export type InputEvent = ButtonInputEvent | AxisInputEvent
return `any --[[{ctype}]]`
end
function memberTypeToString(type : number) : string
if type == 2 then return "boolean" end
if type == 3 then return "number" end
if type == 4 then return "number" end
if type == 5 then return "number" end
if type == 6 then return "string" end
if type == 7 then return "Entity" end
if type == 9 then return "Vec3" end
return "any"
end
function refl()
local out = ""
local lumixAPI_src = ""
local num_structs = LumixReflection.getNumStructs()
for i = 0, num_structs - 1 do
local struct = LumixReflection.getStruct(i)
local name = LumixReflection.getStructName(struct)
out = out .. `declare class {name}\n`
local num_members = LumixReflection.getNumStructMembers(struct)
lumixAPI_src = lumixAPI_src .. `\t{name} : \{ create : () -> {name}, destroy : ({name}) -> () \},\n`
for j = 0, num_members - 1 do
local member = LumixReflection.getStructMember(struct, j)
local member_name = LumixReflection.getStructMemberName(member)
local type = LumixReflection.getStructMemberType(member)
out = out .. `\t{member_name} : {memberTypeToString(type)}\n`
end
out = out .. `end\n\n`
end
local num_funcs = LumixReflection.getNumFunctions()
for i = 1, num_funcs do
local fn = LumixReflection.getFunction(i - 1)
@ -295,12 +355,12 @@ export type InputEvent = ButtonInputEvent | AxisInputEvent
out = out .. "end\n\n"
end
return string.format(tpl, world_src, out, entity_src)
return string.format(tpl, world_src, out, entity_src, lumixAPI_src)
end
local type_defs = refl()
if false then
if true then
return {
name = "Lua type defs",
gui = function()

View File

@ -1,3 +1,8 @@
export type Vec2 = {number}
export type Vec3 = {number}
export type Color = {number}
export type Quat = {number}
export type DVec3 = {number}
declare ImGui: {
AlignTextToFramePadding : () -> (),
Begin : (string, boolean?) -> (boolean, boolean?),
@ -82,19 +87,31 @@ declare class World
end
declare class RaycastHit
position : Vec3
normal : Vec3
entity : Entity
end
declare class SweepHit
position : Vec3
normal : Vec3
entity : Entity
end
declare class GUISystem
enableCursor : (GUISystem, boolean) -> ()
end
declare class AssetBrowser
openEditor : (AssetBrowser, any --[[const char*]]) -> ()
openEditor : (AssetBrowser, string) -> ()
end
declare class SceneView
getViewportRotation : (SceneView) -> any --[[struct Lumix::Quat]]
setViewportRotation : (SceneView, any --[[Quat]]) -> ()
getViewportPosition : (SceneView) -> any --[[struct Lumix::DVec3]]
setViewportPosition : (SceneView, any --[[DVec3]]) -> ()
getViewportRotation : (SceneView) -> Quat
setViewportRotation : (SceneView, Quat) -> ()
getViewportPosition : (SceneView) -> DVec3
setViewportPosition : (SceneView, DVec3) -> ()
end
declare class Model
@ -113,9 +130,9 @@ declare class animation_module
end
declare class gui_module
getRectAt : (gui_module, any --[[Vec2]]) -> any --[[struct Lumix::EntityPtr]]
isOver : (gui_module, any --[[Vec2]], any --[[EntityPtr]]) -> boolean
getSystem : (gui_module) -> GUISystem
getRectAt : (gui_module, Vec2) -> Entity?
isOver : (gui_module, Vec2, Entity?) -> boolean
getSystem : (gui_module) -> any --[[GUISystem *]]
end
declare class lua_script_module
@ -123,7 +140,7 @@ end
declare class audio_module
setMasterVolume : (audio_module, number) -> ()
play : (audio_module, any --[[EntityPtr]], any --[[const char*]], boolean) -> number
play : (audio_module, Entity?, string, boolean) -> number
stop : (audio_module, number) -> ()
isEnd : (audio_module, number) -> boolean
setFrequency : (audio_module, number, number) -> ()
@ -132,16 +149,17 @@ declare class audio_module
end
declare class renderer_module
addDebugCross : (renderer_module, any --[[DVec3]], number, any --[[Color]]) -> ()
addDebugLine : (renderer_module, any --[[DVec3]], any --[[DVec3]], any --[[Color]]) -> ()
addDebugTriangle : (renderer_module, any --[[DVec3]], any --[[DVec3]], any --[[DVec3]], any --[[Color]]) -> ()
setActiveCamera : (renderer_module, any --[[EntityPtr]]) -> ()
addDebugCross : (renderer_module, DVec3, number, Color) -> ()
addDebugLine : (renderer_module, DVec3, DVec3, Color) -> ()
addDebugTriangle : (renderer_module, DVec3, DVec3, DVec3, Color) -> ()
setActiveCamera : (renderer_module, Entity?) -> ()
end
declare class physics_module
raycast : (physics_module, any --[[Vec3]], any --[[Vec3]], number, any --[[EntityPtr]]) -> any --[[struct Lumix::EntityPtr]]
sweepSphere : (physics_module, any --[[DVec3]], number, any --[[Vec3]], number, any --[[EntityPtr]], number) -> any --[[struct Lumix::EntityPtr]]
setGravity : (physics_module, any --[[Vec3]]) -> ()
raycast : (physics_module, Vec3, Vec3, number, Entity?) -> Entity?
raycastEx : (physics_module, Vec3, Vec3, number, any --[[void*]], Entity?, number) -> boolean
sweepSphere : (physics_module, DVec3, number, Vec3, number, any --[[void*]], Entity?, number) -> boolean
setGravity : (physics_module, Vec3) -> ()
end
declare class spline_component
@ -177,7 +195,7 @@ declare class terrain_component
tesselation: number
grid_resolution: number
grass: any
getTerrainNormalAt : (terrain_component, number, number) -> any --[[struct Lumix::Vec3]]
getTerrainNormalAt : (terrain_component, number, number) -> Vec3
getTerrainHeightAt : (terrain_component, number, number) -> number
end
@ -191,7 +209,7 @@ end
declare class decal_component
material: string
half_extents: any
half_extents: Vec3
uv_scale: any
end
@ -207,12 +225,12 @@ declare class point_light_component
intensity: number
fov: number
attenuation: number
color: any
color: Vec3
range: number
end
declare class environment_component
color: any
color: Vec3
intensity: number
indirect_intensity: number
shadow_cascades: any
@ -228,19 +246,19 @@ declare class model_instance_component
enabled: boolean
material: string
source: string
getModel : (model_instance_component) -> Model
getModel : (model_instance_component) -> any --[[Model *]]
end
declare class environment_probe_component
enabled: boolean
inner_range: any
outer_range: any
inner_range: Vec3
outer_range: Vec3
end
declare class reflection_probe_component
enabled: boolean
size: number
half_extents: any
half_extents: Vec3
end
declare class fur_component
@ -256,8 +274,8 @@ end
declare class bone_attachment_component
parent: Entity
relative_position: any
relative_rotation: any
relative_position: Vec3
relative_rotation: Vec3
bone: number
end
@ -272,10 +290,10 @@ declare class rigid_actor_component
material: string
putToSleep : (rigid_actor_component) -> ()
getSpeed : (rigid_actor_component) -> number
getVelocity : (rigid_actor_component) -> any --[[struct Lumix::Vec3]]
applyForce : (rigid_actor_component, any --[[Vec3]]) -> ()
applyImpulse : (rigid_actor_component, any --[[Vec3]]) -> ()
addForceAtPos : (rigid_actor_component, any --[[Vec3]], any --[[Vec3]]) -> ()
getVelocity : (rigid_actor_component) -> Vec3
applyForce : (rigid_actor_component, Vec3) -> ()
applyImpulse : (rigid_actor_component, Vec3) -> ()
addForceAtPos : (rigid_actor_component, Vec3, Vec3) -> ()
end
declare class physical_heightfield_component
@ -292,14 +310,14 @@ declare class physical_controller_component
use_root_motion: boolean
use_custom_gravity: boolean
custom_gravity_acceleration: number
move : (physical_controller_component, any --[[Vec3]]) -> ()
move : (physical_controller_component, Vec3) -> ()
isCollisionDown : (physical_controller_component) -> boolean
getGravitySpeed : (physical_controller_component) -> number
end
declare class lua_script_component
scripts: any
getScriptPath : (lua_script_component, number) -> any --[[struct Lumix::Path]]
getScriptPath : (lua_script_component, number) -> string
end
declare class gui_image_component
@ -331,7 +349,7 @@ end
declare class distance_joint_component
connected_body: Entity
axis_position: any
axis_position: Vec3
damping: number
stiffness: number
tolerance: number
@ -340,8 +358,8 @@ end
declare class hinge_joint_component
connected_body: Entity
axis_position: any
axis_direction: any
axis_position: Vec3
axis_direction: Vec3
damping: number
stiffness: number
use_limit: boolean
@ -350,16 +368,16 @@ end
declare class spherical_joint_component
connected_body: Entity
axis_position: any
axis_direction: any
axis_position: Vec3
axis_direction: Vec3
use_limit: boolean
limit: any
end
declare class d6_joint_component
connected_body: Entity
axis_position: any
axis_direction: any
axis_position: Vec3
axis_direction: Vec3
x_motion: number
y_motion: number
z_motion: number
@ -377,7 +395,7 @@ declare class vehicle_component
current_gear: number
rpm: number
mass: number
center_of_mass: any
center_of_mass: Vec3
moi_multiplier: number
chassis: string
chassis_layer: number
@ -406,13 +424,13 @@ declare class navmesh_agent_component
move_entity: boolean
speed: number
setActive : (navmesh_agent_component, boolean) -> ()
navigate : (navmesh_agent_component, any --[[DVec3]], number, number) -> boolean
navigate : (navmesh_agent_component, DVec3, number, number) -> boolean
cancelNavigation : (navmesh_agent_component) -> ()
drawPath : (navmesh_agent_component) -> ()
end
declare class navmesh_zone_component
extents: any
extents: Vec3
agent_height: number
agent_radius: number
cell_size: number
@ -423,10 +441,10 @@ declare class navmesh_zone_component
detailed: boolean
load : (navmesh_zone_component) -> boolean
drawContours : (navmesh_zone_component) -> ()
drawNavmesh : (navmesh_zone_component, any --[[DVec3]], boolean, boolean, boolean) -> ()
drawNavmesh : (navmesh_zone_component, DVec3, boolean, boolean, boolean) -> ()
drawCompactHeightfield : (navmesh_zone_component) -> ()
drawHeightfield : (navmesh_zone_component) -> ()
generateNavmesh : (navmesh_zone_component) -> any --[[struct Lumix::NavmeshBuildJob *]]
generateNavmesh : (navmesh_zone_component) -> any --[[NavmeshBuildJob *]]
end
declare class lua_script_inline_component
@ -447,11 +465,11 @@ declare class animator_component
use_root_motion: boolean
setFloatInput : (animator_component, number, number) -> ()
setBoolInput : (animator_component, number, boolean) -> ()
getInputIndex : (animator_component, any --[[const char*]]) -> number
getInputIndex : (animator_component, string) -> number
end
declare class physical_instanced_cube_component
half_extents: any
half_extents: Vec3
layer: number
end
@ -486,8 +504,8 @@ declare class Entity
name : string
parent : Entity?
rotation : any
position : any
scale : any
position : Vec3
scale : Vec3
hasComponent : (Entity, any) -> boolean
getComponent : (Entity, any) -> any
destroy : (Entity) -> ()
@ -563,6 +581,9 @@ declare Editor: {
}
declare LumixAPI: {
RaycastHit : { create : () -> RaycastHit, destroy : (RaycastHit) -> () },
SweepHit : { create : () -> SweepHit, destroy : (SweepHit) -> () },
INPUT_KEYCODE_SHIFT: number,
INPUT_KEYCODE_LEFT : number,
INPUT_KEYCODE_RIGHT : number,
@ -581,6 +602,12 @@ end
declare class FunctionBase
end
declare class StructVarBase
end
declare class StructBase
end
declare class ModuleReflection
end
@ -607,6 +634,13 @@ declare LumixReflection: {
getFunction : (number) -> FunctionBase,
getThisTypeName : (FunctionBase) -> string,
getReturnTypeName : (FunctionBase) -> string,
getNumStructs : () -> number,
getStruct : (number) -> StructBase,
getStructName : (StructBase) -> string,
getNumStructMembers : (StructBase) -> number,
getStructMember : (StructBase, number) -> StructVarBase,
getStructMemberName : (StructVarBase) -> string,
getStructMemberType : (StructVarBase) -> number
}
type InputDevice = {

View File

@ -1,47 +1,47 @@
return {
dot = function(a, b)
return a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
end,
mulQuat = function(a, b)
return {
a[4] * b[1] + b[4] * a[1] + a[2] * b[3] - b[2] * a[3],
a[4] * b[2] + b[4] * a[2] + a[3] * b[1] - b[3] * a[1],
a[4] * b[3] + b[4] * a[3] + a[1] * b[2] - b[1] * a[2],
a[4] * b[4] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3]
}
end,
makeQuatFromYaw = function(yaw)
local syaw = math.sin(yaw * 0.5)
local cyaw = math.cos(yaw * 0.5)
return {0, syaw, 0, cyaw }
end,
makeQuatFromPitch = function(pitch)
local spitch = math.sin(pitch * 0.5)
local cpitch = math.cos(pitch * 0.5)
return {-spitch, 0, 0, cpitch}
end,
yawToDir = function(yaw)
return {math.sin(yaw), 0, math.cos(yaw)}
end,
mulVec3Num = function(v, f)
return {v[1] * f, v[2] * f, v[3] * f}
end,
addVec3 = function(a, b)
return {a[1] + b[1], a[2] + b[2], a[3] + b[3]}
end,
subVec3 = function(a, b)
return {a[1] - b[1], a[2] - b[2], a[3] - b[3]}
end,
mulVec3 = function(a, f)
return {a[1] * f, a[2] * f, a[3] * f}
end,
dot = function(a, b)
return a[1] * b[1] + a[2] * b[2] + a[3] * b[3]
end,
mulQuat = function(a, b)
return {
a[4] * b[1] + b[4] * a[1] + a[2] * b[3] - b[2] * a[3],
a[4] * b[2] + b[4] * a[2] + a[3] * b[1] - b[3] * a[1],
a[4] * b[3] + b[4] * a[3] + a[1] * b[2] - b[1] * a[2],
a[4] * b[4] - a[1] * b[1] - a[2] * b[2] - a[3] * b[3]
}
end,
makeQuatFromYaw = function(yaw)
local syaw = math.sin(yaw * 0.5)
local cyaw = math.cos(yaw * 0.5)
return {0, syaw, 0, cyaw }
end,
makeQuatFromPitch = function(pitch)
local spitch = math.sin(pitch * 0.5)
local cpitch = math.cos(pitch * 0.5)
return {-spitch, 0, 0, cpitch}
end,
yawToDir = function(yaw)
return {math.sin(yaw), 0, math.cos(yaw)}
end,
mulVec3Num = function(v, f)
return {v[1] * f, v[2] * f, v[3] * f}
end,
addVec3 = function(a, b)
return {a[1] + b[1], a[2] + b[2], a[3] + b[3]}
end,
subVec3 = function(a, b)
return {a[1] - b[1], a[2] - b[2], a[3] - b[3]}
end,
mulVec3 = function(a, f)
return {a[1] * f, a[2] * f, a[3] * f}
end,
}

View File

@ -591,7 +591,7 @@ struct GridUIVisitor final : reflection::IPropertyVisitor
void visit(const reflection::ArrayProperty& prop) override
{
ImGui::Unindent();
bool is_root_open = ImGui::TreeNodeEx(prop.name, ImGuiTreeNodeFlags_AllowItemOverlap);
bool is_root_open = ImGui::TreeNodeEx(prop.name, ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_DefaultOpen);
if (m_entities.size() > 1)
{
ImGui::Text("Multi-object editing not supported.");

View File

@ -436,7 +436,7 @@ struct CodeEditorImpl final : CodeEditor {
struct Token {
enum Flags {
NONE = 0,
UNDERLINE = 1 << 0
UNDERLINE = 1 << 0,
};
u32 from;
u32 len;
@ -469,7 +469,8 @@ struct CodeEditorImpl final : CodeEditor {
END_GROUP,
REMOVE,
INSERT,
NEW_LINE
NEW_LINE,
MOVE_LINE
};
UndoRecord(IAllocator& allocator) : text(allocator), cursors(allocator) {}
@ -486,6 +487,21 @@ struct CodeEditorImpl final : CodeEditor {
void execute(CodeEditorImpl& editor, bool is_redo) {
++editor.m_version;
switch(type) {
case MOVE_LINE: {
if (from.line < to.line) {
for (i32 line = from.line; line < to.line; ++line) {
swap(editor.m_lines[line], editor.m_lines[line + 1]);
}
}
else {
for (i32 line = from.line; line > to.line; --line) {
swap(editor.m_lines[line], editor.m_lines[line - 1]);
}
}
editor.invalidateTokens(from.line);
editor.invalidateTokens(to.line);
break;
}
case BEGIN_GROUP:
if (!is_redo) editor.m_cursors.copyTo(cursors);
break;
@ -526,6 +542,21 @@ struct CodeEditorImpl final : CodeEditor {
void undo(CodeEditorImpl& editor) {
++editor.m_version;
switch(type) {
case MOVE_LINE: {
if (to.line < from.line) {
for (i32 line = to.line; line < from.line; ++line) {
swap(editor.m_lines[line], editor.m_lines[line + 1]);
}
}
else {
for (i32 line = to.line; line > from.line; --line) {
swap(editor.m_lines[line], editor.m_lines[line - 1]);
}
}
editor.invalidateTokens(from.line);
editor.invalidateTokens(to.line);
break;
}
case BEGIN_GROUP:
cursors.copyTo(editor.m_cursors);
editor.ensurePointVisible(editor.m_cursors[0]);
@ -659,6 +690,23 @@ struct CodeEditorImpl final : CodeEditor {
return (u32)ImGui::CalcTextSize(str, str + cursor.col).x;
}
Token getToken(TextPoint p) {
const Line& line = m_lines[p.line];
for (Token token : line.tokens) {
if (p.col >= (i32)token.from && p.col < i32(token.from + token.len)) return token;
}
return {};
}
StringView toStringView(const Token& token, u32 line) {
const char* str = m_lines[line].value.c_str();
StringView res;
res.begin = str + token.from;
res.end = res.begin + token.len;
return res;
}
void cursorMoved(Cursor& cursor, bool update_virtual) {
cursor.line = clamp(cursor.line, 0, m_lines.size() - 1);
cursor.col = clamp(cursor.col, 0, m_lines[cursor.line].length());
@ -668,6 +716,8 @@ struct CodeEditorImpl final : CodeEditor {
}
if (update_virtual) cursor.virtual_x = computeCursorX(cursor);
m_blink_timer = 0;
m_time_since_cursor_moved = 0;
m_highlighted_str = {};
}
void moveCursorLeft(Cursor& cursor, bool word) {
@ -686,7 +736,7 @@ struct CodeEditorImpl final : CodeEditor {
void ensurePointVisible(TextPoint& cursor, bool center = false) {
if (center) {
m_scroll_y = (cursor.line - (m_last_visible_line - m_first_visible_line) / 2) * ImGui::GetTextLineHeight();
m_scroll_y += (cursor.line - (m_first_visible_line + m_max_visible_lines / 2)) * ImGui::GetTextLineHeight();
m_scroll_y = maximum(m_scroll_y, 0.f);
return;
}
@ -700,6 +750,47 @@ struct CodeEditorImpl final : CodeEditor {
}
}
void moveLinesUp() {
TextPoint from = m_cursors[0];
TextPoint to = m_cursors[0].sel;
if (from > to) swap(from, to);
if (to.col == 0 && to.line > from.line) --to.line;
if (from.line == 0) return;
beginUndoGroup();
m_cursors.resize(1);
UndoRecord& r = pushUndo();
r.type = UndoRecord::MOVE_LINE;
r.from = from;
r.to = to;
--r.from.line;
r.execute(*this, false);
--m_cursors[0].line;
--m_cursors[0].sel.line;
endUndoGroup();
}
void moveLinesDown() {
m_cursors.resize(1);
TextPoint from = m_cursors[0];
TextPoint to = m_cursors[0].sel;
if (from > to) swap(from, to);
if (to.col == 0 && to.line > from.line) --to.line;
if (to.line == m_lines.size() - 1) return;
beginUndoGroup();
m_cursors.resize(1);
UndoRecord& r = pushUndo();
r.type = UndoRecord::MOVE_LINE;
r.from = to;
r.to = from;
++r.from.line;
r.execute(*this, false);
++m_cursors[0].line;
++m_cursors[0].sel.line;
endUndoGroup();
}
void moveCursorUp(Cursor& cursor, u32 line_count = 1) {
const char* line_str = m_lines[cursor.line].value.c_str();
cursor.line = maximum(0, cursor.line - line_count);
@ -755,15 +846,13 @@ struct CodeEditorImpl final : CodeEditor {
void moveCursorBegin(Cursor& cursor, bool doc) {
if (doc) cursor.line = 0;
if (cursor.col == 0) {
const String& line = m_lines[cursor.line].value;
while (cursor.col < (i32)line.length() && !isWordChar(line[cursor.col])) {
++cursor.col;
}
}
else {
cursor.col = 0;
const i32 prev_col = cursor.col;
const String& line = m_lines[cursor.line].value;
cursor.col = 0;
while (cursor.col < (i32)line.length() && !isWordChar(line[cursor.col])) {
++cursor.col;
}
if (cursor.col >= prev_col && prev_col != 0) cursor.col = 0;
cursorMoved(cursor, true);
if (&cursor == &m_cursors[0]) ensurePointVisible(cursor);
}
@ -1026,7 +1115,7 @@ struct CodeEditorImpl final : CodeEditor {
new_cursor.sel.col = i32(found - m_lines[line].value.c_str());
new_cursor.col = new_cursor.sel.col + sel_view.size();
new_cursor.virtual_x = computeCursorX(new_cursor);
ensurePointVisible(new_cursor, true);
ensurePointVisible(new_cursor);
return;
}
++line;
@ -1097,25 +1186,30 @@ struct CodeEditorImpl final : CodeEditor {
TextPoint getLeftWord(TextPoint point) {
TextPoint p = getLeft(point);
bool is_word = isWordChar(getChar(p));
p = getLeft(p);
while (isWordChar(getChar(p)) == is_word) {
if (p.col == 0) return p;
const bool is_word = isWordChar(getChar(p));
for (;;) {
p = getLeft(p);
if (p.line == 0 && p.col == 0) return p;
char c = getChar(p);
if (isWordChar(c) != is_word) {
p = getRight(p);
return p;
}
if (p.col == 0) return p;
}
return getRight(p);
return p;
}
TextPoint getRightWord(TextPoint point) {
TextPoint p = getRight(point);
bool is_word = isWordChar(getChar(p));
p = getRight(p);
while (isWordChar(getChar(p)) == is_word) {
TextPoint p = point;
const bool is_word = isWordChar(getChar(p));
char c;
do {
p = getRight(p);
c = getChar(p);
if (c == '\n') return p;
if (p.line == m_lines.size() - 1 && p.col == m_lines.back().length()) return p;
}
} while (isWordChar(c) == is_word);
return p;
}
@ -1450,9 +1544,9 @@ struct CodeEditorImpl final : CodeEditor {
// text
m_first_visible_line = i32(m_scroll_y / line_height);
float visible_lines = content_size.y / line_height;
m_max_visible_lines = i32(content_size.y / line_height);
m_first_visible_line = clamp(m_first_visible_line, 0, m_lines.size() - 1);
m_last_visible_line = minimum(m_first_visible_line + i32(visible_lines), m_lines.size() - 1);
m_last_visible_line = minimum(m_first_visible_line + i32(m_max_visible_lines), m_lines.size() - 1);
{
PROFILE_BLOCK("tokenize");
@ -1468,10 +1562,16 @@ struct CodeEditorImpl final : CodeEditor {
const char* str = m_lines[j].value.c_str();
ImVec2 p = text_area_pos + ImVec2(0, line_offset_y);
for (const Token& t : m_lines[j].tokens) {
dl->AddText(p, m_token_colors[t.type], str + t.from, str + t.from + t.len);
ImVec2 start_p = p;
p.x += ImGui::CalcTextSize(str + t.from, str + t.from + t.len).x;
if (equalStrings(toStringView(t, j), m_highlighted_str)) {
dl->AddRectFilled(start_p, p + ImVec2(0, line_height), IM_COL32(0x50, 0x50, 0x50, 0x7f));
}
dl->AddText(start_p, m_token_colors[t.type], str + t.from, str + t.from + t.len);
if (t.flags & Token::UNDERLINE) {
if (m_handle_input && ImGui::IsMouseHoveringRect(start_p, p + ImVec2(0, line_height))) {
const Underline* underline = getUnderline(j, t);
@ -1485,6 +1585,19 @@ struct CodeEditorImpl final : CodeEditor {
profiler::pushInt("Num tokens", visible_tokens);
// cursors
float prev_since_cursor_moved = m_time_since_cursor_moved;
m_time_since_cursor_moved += io.DeltaTime;
if (m_time_since_cursor_moved > 0.5f && prev_since_cursor_moved <= 0.5f) {
m_highlighted_str = toStringView(getToken(m_cursors[0]), m_cursors[0].line);
bool empty = true;
for (const char* c = m_highlighted_str.begin; c != m_highlighted_str.end; ++c) {
if (isWordChar(*c)) {
empty = false;
break;
}
}
if (empty) m_highlighted_str = {};
}
m_blink_timer += io.DeltaTime;
m_blink_timer = fmodf(m_blink_timer, 1.f);
bool draw_cursors = m_blink_timer < 0.6f;
@ -1495,8 +1608,8 @@ struct CodeEditorImpl final : CodeEditor {
if (draw_cursors) dl->AddRectFilled(cursor_pos, cursor_pos + ImVec2(1, line_height), code_color);
if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) moveCursorLeft(c, io.KeyCtrl);
else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) moveCursorRight(c, io.KeyCtrl);
else if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) moveCursorUp(c);
else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) moveCursorDown(c);
else if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) io.KeyAlt ? moveLinesUp() : moveCursorUp(c);
else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) io.KeyAlt ? moveLinesDown() : moveCursorDown(c);
else if (ImGui::IsKeyPressed(ImGuiKey_End)) moveCursorEnd(c, io.KeyCtrl);
else if (ImGui::IsKeyPressed(ImGuiKey_Home)) moveCursorBegin(c, io.KeyCtrl);
}
@ -1658,6 +1771,8 @@ struct CodeEditorImpl final : CodeEditor {
i32 m_first_untokenized_line = 0;
TagAllocator m_allocator;
float m_blink_timer = 0;
float m_time_since_cursor_moved = 0;
StringView m_highlighted_str;
StudioApp& m_app;
Array<Line> m_lines;
Array<Underline> m_underlines;
@ -1665,6 +1780,7 @@ struct CodeEditorImpl final : CodeEditor {
Array<Cursor> m_cursors;
i32 m_first_visible_line = 0;
i32 m_last_visible_line = 0;
i32 m_max_visible_lines = 0;
Tokenizer m_tokenizer = nullptr;
Span<const u32> m_token_colors;
u32 m_version = 0;

View File

@ -410,10 +410,38 @@ static i32 LUA_getNumFunctions() {
return reflection::allFunctions().size();
}
static i32 LUA_getNumStructs() {
return reflection::allStructs().size();
}
static reflection::FunctionBase* LUA_getFunction(i32 idx) {
return reflection::allFunctions()[idx];
}
static reflection::StructBase* LUA_getStruct(i32 idx) {
return reflection::allStructs()[idx];
}
static const char* LUA_getStructName(reflection::StructBase* str) {
return str->name;
}
static i32 LUA_getNumStructMembers(reflection::StructBase* str) {
return str->members.size();
}
static reflection::StructVarBase* LUA_getStructMember(reflection::StructBase* str, u32 idx) {
return str->members[idx];
}
static u32 LUA_getStructMemberType(reflection::StructVarBase* var) {
return (u32)var->getType().type;
}
static const char* LUA_getStructMemberName(reflection::StructVarBase* var) {
return var->name;
}
static i32 LUA_getNextModule(lua_State* L) {
reflection::Module* module = LuaWrapper::checkArg<reflection::Module*>(L, 1);
if (module->next) lua_pushlightuserdata(L, module->next);
@ -953,6 +981,14 @@ void registerEngineAPI(lua_State* L, Engine* engine)
LuaWrapper::createSystemFunction(L, "LumixReflection", "getNumFunctions", &LuaWrapper::wrap<LUA_getNumFunctions>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getFunction", &LuaWrapper::wrap<LUA_getFunction>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getNumStructs", &LuaWrapper::wrap<LUA_getNumStructs>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getStruct", &LuaWrapper::wrap<LUA_getStruct>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getStructName", &LuaWrapper::wrap<LUA_getStructName>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getNumStructMembers", &LuaWrapper::wrap<LUA_getNumStructMembers>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getStructMember", &LuaWrapper::wrap<LUA_getStructMember>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getStructMemberName", &LuaWrapper::wrap<LUA_getStructMemberName>);
LuaWrapper::createSystemFunction(L, "LumixReflection", "getStructMemberType", &LuaWrapper::wrap<LUA_getStructMemberType>);
LuaWrapper::createSystemFunction(L, "LumixAPI", "networkRead", &LUA_networkRead);
LuaWrapper::createSystemFunction(L, "LumixAPI", "packU32", &LUA_packU32);
LuaWrapper::createSystemFunction(L, "LumixAPI", "unpackU32", &LUA_unpackU32);

View File

@ -38,6 +38,11 @@ Array<FunctionBase*>& allFunctions() {
return fncs;
}
Array<StructBase*>& allStructs() {
static Array<StructBase*> structs(getGlobalAllocator());
return structs;
}
ComponentBase::ComponentBase(IAllocator& allocator)
: props(allocator)
, functions(allocator)

View File

@ -326,7 +326,7 @@ StringView getTypeName()
struct Variant {
Variant() { type = I32; i = 0; }
enum Type {
enum Type : u32 {
VOID,
PTR,
BOOL,
@ -380,6 +380,7 @@ struct TypeDescriptor {
template <typename T> struct VariantTag {};
template <typename T> inline Variant::Type _getVariantType(VariantTag<T*>) { return Variant::PTR; }
template <typename T> inline Variant::Type _getVariantType(VariantTag<T>) { return Variant::PTR; }
inline Variant::Type _getVariantType(VariantTag<void>) { return Variant::VOID; }
inline Variant::Type _getVariantType(VariantTag<bool>) { return Variant::BOOL; }
inline Variant::Type _getVariantType(VariantTag<i32>) { return Variant::I32; }
@ -435,6 +436,7 @@ inline EntityPtr fromVariant(int i, Span<Variant> args, VariantTag<EntityPtr>) {
inline EntityRef fromVariant(int i, Span<Variant> args, VariantTag<EntityRef>) { return (EntityRef)args[i].e; }
inline void* fromVariant(int i, Span<Variant> args, VariantTag<void*>) { return args[i].ptr; }
template <typename T> inline T* fromVariant(int i, Span<Variant> args, VariantTag<T*>) { return (T*)args[i].ptr; }
template <typename T> inline T& fromVariant(int i, Span<Variant> args, VariantTag<T>) { return *(T*)args[i].ptr; }
template <typename... Args>
struct VariantCaller {
@ -566,7 +568,75 @@ struct Function<R (C::*)(Args...) const> : FunctionBase
}
};
struct StructVarBase {
virtual ~StructVarBase() {}
virtual bool set(void* obj, Span<const u8> mem) = 0;
virtual bool get(const void* obj, Span<u8> mem) = 0;
template <typename T> T get(void* obj) {
T res;
get(obj, Span((u8*)&res, sizeof(res)));
return res;
}
template <typename T> void set(void* obj, T val) {
set(obj, Span((const u8*)&val, sizeof(val)));
}
virtual TypeDescriptor getType() const = 0;
const char* name;
};
template <auto Getter>
struct StructVar : StructVarBase {
using T = typename ResultOf<decltype(Getter)>::Type;
using C = typename ClassOf<decltype(Getter)>::Type;
TypeDescriptor getType() const override {
return toTypeDescriptor<T>();
}
bool set(void* obj, Span<const u8> mem) override {
C* inst = (C*)obj;
auto& v = inst->*Getter;
if (sizeof(v) != mem.length()) return false;
memcpy(&v, mem.begin(), sizeof(v));
return true;
}
bool get(const void* obj, Span<u8> mem) override {
C* inst = (C*)obj;
auto& v = inst->*Getter;
if (sizeof(v) != mem.length()) return false;
memcpy(mem.begin(), &v, sizeof(v));
return true;
}
};
struct StructBase {
StructBase() : allocator(getGlobalAllocator()), members(getGlobalAllocator()) {}
virtual ~StructBase() {}
virtual void* createInstance(IAllocator& allocator) = 0;
virtual void destroyInstance(void* obj, IAllocator& allocator) = 0;
template <auto Getter>
StructBase& member(const char* name) {
StructVar<Getter>* member = LUMIX_NEW(allocator, StructVar<Getter>);
member->name = name;
members.push(member);
return *this;
}
IAllocator& allocator;
const char* name;
Array<StructVarBase*> members;
};
LUMIX_ENGINE_API Array<FunctionBase*>& allFunctions();
LUMIX_ENGINE_API Array<StructBase*>& allStructs();
template <typename F>
auto& function(F func, const char* decl_code, const char* name)
@ -579,6 +649,18 @@ auto& function(F func, const char* decl_code, const char* name)
return ret;
}
template <typename S>
auto& structure(const char* name)
{
static struct : StructBase {
void* createInstance(IAllocator& allocator) override { return LUMIX_NEW(allocator, S); }
void destroyInstance(void* obj, IAllocator& allocator) override { LUMIX_DELETE(allocator, (S*)obj); }
} ret;
ret.name = name;
allStructs().push(&ret);
return ret;
}
struct LUMIX_ENGINE_API ComponentBase {
ComponentBase(IAllocator& allocator);

View File

@ -39,9 +39,10 @@ void EntityMap::set(EntityRef src, EntityRef dst) {
m_map[src.index] = dst;
}
World::~World() = default;
World::~World() {
// release modules first, since they can access world
m_modules.clear();
}
World::World(Engine& engine)
: m_allocator(engine.getAllocator(), "world")

View File

@ -124,7 +124,16 @@ static void toVariant(reflection::Variant::Type type, lua_State* L, int idx, ref
case reflection::Variant::FLOAT: val = LuaWrapper::checkArg<float>(L, idx); break;
case reflection::Variant::ENTITY: val = LuaWrapper::checkArg<EntityPtr>(L, idx); break;
case reflection::Variant::VEC2: val = LuaWrapper::checkArg<Vec2>(L, idx); break;
case reflection::Variant::COLOR:
case reflection::Variant::COLOR: {
if (LuaWrapper::isType<Vec4>(L, idx)) {
Vec4 c = LuaWrapper::toType<Vec4>(L, idx);
val = Color(u8(c.r * 255), u8(c.g * 255), u8(c.b * 255), u8(c.a * 255));
break;
}
Vec3 c = LuaWrapper::checkArg<Vec3>(L, idx);
val = Color(u8(c.r * 255), u8(c.g * 255), u8(c.b * 255), 0xff);
break;
}
case reflection::Variant::VEC3: val = LuaWrapper::checkArg<Vec3>(L, idx); break;
case reflection::Variant::DVEC3: val = LuaWrapper::checkArg<DVec3>(L, idx); break;
case reflection::Variant::QUAT: val = LuaWrapper::checkArg<Quat>(L, idx); break;
@ -241,9 +250,129 @@ static int luaCmpMethodClosure(lua_State* L) {
return push(L, res, f->getReturnTypeName());
}
static int lua_struct_var_setter(lua_State* L) {
LuaWrapper::checkTableArg(L, 1); // self
const char* prop_name = LuaWrapper::checkArg<const char*>(L, 2);
int type = lua_getfield(L, 1, "_value");
if (type != LUA_TLIGHTUSERDATA) luaL_argerror(L, 1, "invalid object");
void* inst = lua_tolightuserdata(L, -1);
lua_pop(L, 1);
reflection::StructBase* s = LuaWrapper::toType<reflection::StructBase*>(L, lua_upvalueindex(1));
for (reflection::StructVarBase* var : s->members) {
if (equalStrings(var->name, prop_name)) {
reflection::TypeDescriptor td = var->getType();
switch (td.type) {
case reflection::Variant::DVEC3: {
const DVec3& v = LuaWrapper::checkArg<DVec3>(L, 2);
var->set(inst, v);
return 0;
}
case reflection::Variant::VEC3: {
const Vec3& v = LuaWrapper::checkArg<Vec3>(L, 2);
var->set(inst, v);
return 0;
}
case reflection::Variant::FLOAT: {
const float v = LuaWrapper::checkArg<float>(L, 2);
var->set(inst, v);
return 0;
}
default:
ASSERT(false);
// TODO
return 0;
}
}
}
return 0;
}
static int lua_struct_var_getter(lua_State* L) {
LuaWrapper::checkTableArg(L, 1); // self
const char* prop_name = LuaWrapper::checkArg<const char*>(L, 2);
int type = lua_getfield(L, 1, "_value");
if (type != LUA_TLIGHTUSERDATA) luaL_argerror(L, 1, "invalid object");
void* inst = lua_tolightuserdata(L, -1);
lua_pop(L, 1);
reflection::StructBase* s = LuaWrapper::toType<reflection::StructBase*>(L, lua_upvalueindex(1));
for (reflection::StructVarBase* var : s->members) {
if (equalStrings(var->name, prop_name)) {
reflection::TypeDescriptor td = var->getType();
switch (td.type) {
case reflection::Variant::DVEC3: {
LuaWrapper::push(L, var->get<DVec3>(inst));
return 1;
}
case reflection::Variant::VEC3: {
LuaWrapper::push(L, var->get<Vec3>(inst));
return 1;
}
case reflection::Variant::FLOAT: {
LuaWrapper::push(L, var->get<float>(inst));
return 1;
}
default:
ASSERT(false);
// TODO
return 0;
}
}
}
return 0;
}
static void createClasses(lua_State* L) {
LuaWrapper::DebugGuard guard(L);
lua_getglobal(L, "LumixAPI");
for (auto* s : reflection::allStructs()) {
if (LuaWrapper::getField(L, -1, s->name) != LUA_TTABLE) { // [LumixAPI, obj|nil ]
lua_pop(L, 1); // [LumixAPI]
lua_newtable(L); // [LumixAPI, obj]
lua_pushvalue(L, -1); // [LumixAPI, obj, obj]
lua_setfield(L, -3, s->name); // [LumixAPI, obj]
lua_pushlightuserdata(L, s); // [LumixAPI, obj, refl::struct]
lua_pushcclosure(L, lua_struct_var_getter, "struct_var_getter", 1); // [LumixAPI, obj, var_getter]
lua_setfield(L, -2, "__index"); // [LumixAPI, obj]
lua_pushlightuserdata(L, s); // [LumixAPI, obj, refl::struct]
lua_pushcclosure(L, lua_struct_var_setter, "struct_var_setter", 1); // [LumixAPI, obj, var_setter]
lua_setfield(L, -2, "__newindex"); // [LumixAPI, obj]
lua_pushvalue(L, -1); // [LumixAPI, obj, obj]
auto creator = [](lua_State* L) -> int {
auto* s = LuaWrapper::getClosureObject<reflection::StructBase>(L);
void* obj = s->createInstance(getGlobalAllocator());
LuaWrapper::pushObject(L, obj, s->name);
return 1;
};
auto destroyer = [](lua_State* L) -> int {
auto* s = LuaWrapper::getClosureObject<reflection::StructBase>(L);
LuaWrapper::checkTableArg(L, 1);
void* obj;
if (!LuaWrapper::checkField(L, 1, "_value", &obj)) {
luaL_argerror(L, 1, "expected object");
}
s->destroyInstance(obj, getGlobalAllocator());
return 0;
};
lua_pushlightuserdata(L, s); // [LumixAPI, obj, obj, refl::struct]
lua_pushcclosure(L, creator, "create", 1); // [LumixAPI, obj, obj, closure]
lua_setfield(L, -2, "create"); // [LumixAPI, obj, obj]
lua_pushlightuserdata(L, s); // [LumixAPI, obj, obj, refl::struct]
lua_pushcclosure(L, destroyer, "destroy", 1); // [LumixAPI, obj, obj, closure]
lua_setfield(L, -2, "destroy"); // [LumixAPI, obj, obj]
lua_pop(L, 1); // [LumixAPI, obj ]
}
lua_pop(L, 1);
}
for (auto* f : reflection::allFunctions()) {
char tmp_obj_type_name[128];
copyString(Span(tmp_obj_type_name), f->getThisTypeName());

View File

@ -2392,7 +2392,7 @@ struct PhysicsModuleImpl final : PhysicsModule
PhysicsModuleImpl* module;
};
EntityPtr sweepSphere(const DVec3& pos, float radius, const Vec3& dir, float distance, EntityPtr ignored, i32 layer) override {
bool sweepSphere(const DVec3& pos, float radius, const Vec3& dir, float distance, SweepHit& result, EntityPtr ignored, i32 layer) override {
PxSweepBuffer hit;
physx::PxSphereGeometry sphere(radius);
physx::PxTransform transform(toPhysx(pos), physx::PxIdentity);
@ -2403,9 +2403,11 @@ struct PhysicsModuleImpl final : PhysicsModule
PxQueryFilterData filter_data;
filter_data.flags = PxQueryFlag::eDYNAMIC | PxQueryFlag::eSTATIC | PxQueryFlag::ePREFILTER;
if (!m_scene->sweep(sphere, transform, toPhysx(dir), distance, hit, physx::PxHitFlag::eDEFAULT, filter_data, &filter)) return INVALID_ENTITY;
if (!hit.hasBlock) return INVALID_ENTITY;
const EntityRef hit_entity = {(int)(intptr_t)hit.block.actor->userData};
return hit_entity;
if (!hit.hasBlock) return false;
result.entity = EntityPtr{(int)(intptr_t)hit.block.actor->userData};
result.position = fromPhysx(hit.block.position);
result.normal = fromPhysx(hit.block.normal);
return true;
}
bool raycastEx(const Vec3& origin,
@ -3961,9 +3963,20 @@ void PhysicsModule::reflect() {
return "N/A";
}
};
reflection::structure<RaycastHit>("RaycastHit")
.member<&RaycastHit::position>("position")
.member<&RaycastHit::normal>("normal")
.member<&RaycastHit::entity>("entity");
reflection::structure<SweepHit>("SweepHit")
.member<&SweepHit::position>("position")
.member<&SweepHit::normal>("normal")
.member<&SweepHit::entity>("entity");
LUMIX_MODULE(PhysicsModuleImpl, "physics")
.LUMIX_FUNC(raycast)
.LUMIX_FUNC(raycastEx)
.LUMIX_FUNC(sweepSphere)
.LUMIX_FUNC(setGravity)
.LUMIX_CMP(D6Joint, "d6_joint", "Physics / Joint / D6")

View File

@ -40,13 +40,17 @@ struct World;
template <typename T> struct DelegateList;
struct RaycastHit
{
struct RaycastHit {
Vec3 position;
Vec3 normal;
EntityPtr entity;
};
struct SweepHit {
Vec3 position;
Vec3 normal;
EntityPtr entity;
};
struct LUMIX_PHYSICS_API PhysicsModule : IModule
{
@ -91,7 +95,7 @@ struct LUMIX_PHYSICS_API PhysicsModule : IModule
virtual void forceUpdateDynamicActors(float time_delta) = 0;
virtual const Array<EntityRef>& getDynamicActors() = 0;
virtual void render() = 0;
virtual EntityPtr sweepSphere(const DVec3& pos, float radius, const Vec3& dir, float distance, EntityPtr ignored, i32 layer) = 0;
virtual bool sweepSphere(const DVec3& pos, float radius, const Vec3& dir, float distance, SweepHit& result, EntityPtr ignored, i32 layer) = 0;
virtual EntityPtr raycast(const Vec3& origin, const Vec3& dir, float distance, EntityPtr ignore_entity) = 0;
virtual bool raycastEx(const Vec3& origin, const Vec3& dir, float distance, RaycastHit& result, EntityPtr ignored, int layer) = 0;
virtual void setGravity(const Vec3& gravity) = 0;