diff --git a/Content/Content.vcxitems b/Content/Content.vcxitems
index 874a615d5..93303f353 100644
--- a/Content/Content.vcxitems
+++ b/Content/Content.vcxitems
@@ -600,6 +600,7 @@
+
diff --git a/Content/Content.vcxitems.filters b/Content/Content.vcxitems.filters
index 951aba848..d0595d53c 100644
--- a/Content/Content.vcxitems.filters
+++ b/Content/Content.vcxitems.filters
@@ -569,6 +569,9 @@
scripts\character_controller\assets
+
+ scripts\character_controller\assets
+
diff --git a/Content/Documentation/ScriptingAPI-Documentation.md b/Content/Documentation/ScriptingAPI-Documentation.md
index 403f28aad..dd6472838 100644
--- a/Content/Documentation/ScriptingAPI-Documentation.md
+++ b/Content/Documentation/ScriptingAPI-Documentation.md
@@ -240,6 +240,8 @@ Specify Sprite properties, like position, size, etc.
- DisableMirror()
- EnableBackgroundBlur(opt float mip = 0)
- DisableBackgroundBlur()
+- SetMaskAlphaRange(float start, end)
+- GetMaskAlphaRange() : float start, end
- [outer]STENCILMODE_DISABLED : int
- [outer]STENCILMODE_EQUAL : int
@@ -1400,7 +1402,7 @@ This is the main entry point and manages the lifetime of the application.
- [void-constructor]Application()
- GetContent() : Resource? result
- GetActivePath() : RenderPath? result
-- SetActivePath(RenderPath path, opt float fadeSeconds,fadeColorR,fadeColorG,fadeColorB)
+- SetActivePath(RenderPath path, opt float fadeSeconds, opt int fadeColorR = 0, fadeColorG = 0, fadeColorB = 0)
- SetFrameSkip(bool enabled) -- enable/disable frame skipping in fixed update
- SetTargetFrameRate(float fps) -- set target frame rate for fixed update and variable rate update when frame rate is locked
- SetFrameRateLock(bool enabled) -- if enabled, variable rate update will use a fixed delta time
@@ -1416,6 +1418,7 @@ This is the main entry point and manages the lifetime of the application.
- GetCanvas() : Canvas canvas -- returns a copy of the application's current canvas
- SetCanvas(Canvas canvas) -- applies the specified canvas to the application
- Exit() -- Closes the program
+- IsFaded() -- returns true when fadeout is full (fadeout can be set when switching paths with SetActivePath())
- [outer]SetProfilerEnabled(bool enabled)
### RenderPath
@@ -1507,8 +1510,13 @@ Tonemap = {
It is a RenderPath2D but one that internally manages resource loading and can display information about the process.
It inherits functions from RenderPath2D.
- [constructor]LoadingScreen()
-- AddLoadingTask(string taskScript)
-- OnFinished(string taskScript)
+- AddLoadModelTask(string fileName, Matrix matrix) : Entity -- Adds a scene loading task into the global scene and returns the root entity handle immediately. The loading task will be started asynchronously when the LoadingScreen is activated by the Application.
+- AddLoadModelTask(Scene scene, string fileName, Matrix matrix) : Entity -- Adds a scene loading task into the specified scene and returns the root entity handle immediately. The loading task will be started asynchronously when the LoadingScreen is activated by the Application.
+- AddRenderPathActivationTask(RenderPath path, opt float fadeSeconds = 0, opt int fadeR = 0,fadeG = 0,fadeB = 0) -- loads resources of a RenderPath and activates it after all loading tasks have finished
+- IsFinished() : bool -- returns true when all loading tasks have finished
+- GetProgress() : int -- returns percentage of loading complete (0% - 100%)
+- SetBackgroundTexture(Texture tex) -- set a full screen background texture that wil be displayed when loading screen is active
+- GetBackgroundTexture() : Texture
### Primitives
diff --git a/Content/scripts/character_controller/assets/loadingscreen.png b/Content/scripts/character_controller/assets/loadingscreen.png
new file mode 100644
index 000000000..764393156
Binary files /dev/null and b/Content/scripts/character_controller/assets/loadingscreen.png differ
diff --git a/Content/scripts/character_controller/character_controller.lua b/Content/scripts/character_controller/character_controller.lua
index f85e62453..65f2634e8 100644
--- a/Content/scripts/character_controller/character_controller.lua
+++ b/Content/scripts/character_controller/character_controller.lua
@@ -278,9 +278,7 @@ local footprint_texture = Texture(script_dir() .. "assets/footprint.dds")
local footprints = {}
local animations = {}
-local function LoadAnimations(model_name)
- local anim_scene = Scene()
- LoadModel(anim_scene, model_name)
+local function LoadAnimations(anim_scene)
animations = {
IDLE = anim_scene.Entity_FindByName("idle"),
WALK = anim_scene.Entity_FindByName("walk"),
@@ -292,7 +290,6 @@ local function LoadAnimations(model_name)
DANCE = anim_scene.Entity_FindByName("dance"),
WAVE = anim_scene.Entity_FindByName("wave"),
}
- return anim_scene
end
local character_capsules = {}
@@ -300,7 +297,7 @@ local voxelgrid = VoxelGrid(128,32,128)
voxelgrid.SetVoxelSize(0.25)
voxelgrid.SetCenter(Vector(0,0.1,0))
-local function Character(model_name, start_position, face, controllable, anim_scene)
+local function Character(model_entity, start_position, face, controllable, anim_scene)
local self = {
model = INVALID_ENTITY,
target_rot_horizontal = 0,
@@ -350,7 +347,7 @@ local function Character(model_name, start_position, face, controllable, anim_sc
dialogs = {},
next_dialog = 1,
- Create = function(self, model_name, start_position, face, controllable)
+ Create = function(self, model_entity, start_position, face, controllable)
self.start_position = start_position
self.face = face
self.face_next = face
@@ -360,7 +357,7 @@ local function Character(model_name, start_position, face, controllable, anim_sc
else
self.layerMask = Layers.NPC
end
- self.model = LoadModel(model_name)
+ self.model = model_entity
local layer = scene.Component_GetLayer(self.model)
layer.SetLayerMask(self.layerMask)
@@ -991,7 +988,7 @@ local function Character(model_name, start_position, face, controllable, anim_sc
}
- self:Create(model_name, start_position, face, controllable)
+ self:Create(model_entity, start_position, face, controllable)
return self
end
@@ -1167,38 +1164,14 @@ local function ThirdPersonCamera(character)
return self
end
-ClearWorld()
-LoadModel(script_dir() .. "assets/level.wiscene")
---LoadModel(script_dir() .. "assets/terrain.wiscene")
---LoadModel(script_dir() .. "assets/waypoints.wiscene", matrix.Translation(Vector(1,0,2)))
---dofile(script_dir() .. "../dungeon_generator/dungeon_generator.lua")
-
-scene.VoxelizeScene(voxelgrid, false, FILTER_NAVIGATION_MESH | FILTER_COLLIDER, ~(Layers.Player | Layers.NPC)) -- player and npc layers not included in voxelization
-
-local anim_scene = LoadAnimations(script_dir() .. "assets/animations.wiscene")
-
-local player = Character(script_dir() .. "assets/character.wiscene", Vector(0,0.5,0), Vector(0,0,1), true, anim_scene)
-local npcs = {
- -- Patrolling NPC IDs: 1,2,3
- Character(script_dir() .. "assets/character.wiscene", Vector(4,0.1,4), Vector(0,0,-1), false, anim_scene),
- Character(script_dir() .. "assets/character.wiscene", Vector(-8,1,4), Vector(-1,0,0), false, anim_scene),
- Character(script_dir() .. "assets/character.wiscene", Vector(-2,0.1,8), Vector(-1,0,0), false, anim_scene),
-
- -- stationary NPC IDs: 3,4....
- Character(script_dir() .. "assets/character.wiscene", Vector(-1,0.1,-6), Vector(0,0,1), false, anim_scene),
- --Character(script_dir() .. "assets/character.wiscene", Vector(10.8,0.1,4.1), Vector(0,0,-1), false, anim_scene),
- --Character(script_dir() .. "assets/character.wiscene", Vector(11.1,4,7.2), Vector(-1,0,0), false, anim_scene),
-}
-
-local camera = ThirdPersonCamera(player)
-
--- Main loop:
runProcess(function()
-- We will override the render path so we can invoke the script from Editor and controls won't collide with editor scripts
-- Also save the active component that we can restore when ESCAPE is pressed
local prevPath = application.GetActivePath()
local path = RenderPath3D()
+ local loadingscreen = LoadingScreen()
+
--path.SetLightShaftsEnabled(true)
path.SetLightShaftsStrength(0.01)
path.SetAO(AO_MSAO)
@@ -1208,18 +1181,343 @@ runProcess(function()
path.SetOutlineThickness(1.7)
path.SetOutlineColor(0,0,0,0.6)
path.SetBloomThreshold(5)
- application.SetActivePath(path)
--application.SetInfoDisplay(false)
- application.SetFPSDisplay(true)
+ --application.SetFPSDisplay(true)
--path.SetResolutionScale(0.75)
--path.SetFSR2Enabled(true)
--path.SetFSR2Preset(FSR2_Preset.Performance)
--SetProfilerEnabled(true)
+ -- Configure a simple loading progress bar:
+ local loadingbar = Sprite()
+ loadingbar.SetMaskTexture(texturehelper.CreateGradientTexture(
+ GradientType.Linear,
+ 2048, 1,
+ Vector(0, 0), Vector(1, 0),
+ GradientFlags.Inverse,
+ "111r"
+ ))
+ local loadingbarparams = loadingbar.GetParams()
+ loadingbarparams.SetColor(Vector(1,0.2,0.2,1))
+ loadingbarparams.SetBlendMode(BLENDMODE_ALPHA)
+ loadingscreen.AddSprite(loadingbar)
+ loadingscreen.SetBackgroundTexture(Texture(script_dir() .. "assets/loadingscreen.png"))
+
+ -- All LoadModel tasks are started by the LoadingScreen asynchronously, but we get back root Entity handles immediately:
+ local anim_scene = Scene()
+ loadingscreen.AddLoadModelTask(anim_scene, script_dir() .. "assets/animations.wiscene")
+ local loading_scene = Scene()
+ local character_entities = {
+ loadingscreen.AddLoadModelTask(loading_scene, script_dir() .. "assets/character.wiscene"),
+ loadingscreen.AddLoadModelTask(loading_scene, script_dir() .. "assets/character.wiscene"),
+ loadingscreen.AddLoadModelTask(loading_scene, script_dir() .. "assets/character.wiscene"),
+ loadingscreen.AddLoadModelTask(loading_scene, script_dir() .. "assets/character.wiscene"),
+ loadingscreen.AddLoadModelTask(loading_scene, script_dir() .. "assets/character.wiscene"),
+ }
+ loadingscreen.AddLoadModelTask(loading_scene, script_dir() .. "assets/level.wiscene")
+ loadingscreen.AddRenderPathActivationTask(path, application, 0.5)
+ application.SetActivePath(loadingscreen, 0.5) -- activate and switch to loading screen
+
+ -- Because we are in a runProcess, we can block loading screen like this while application is still running normally:
+ -- Meanwhile, we can update the progress bar sprite
+ while not loadingscreen.IsFinished() do
+ update() -- gives back control for application for one frame, script waits until next update() phase
+ local canvas = application.GetCanvas()
+ loadingbarparams.SetPos(Vector(50, canvas.GetLogicalHeight() * 0.8))
+ loadingbarparams.SetSize(Vector(canvas.GetLogicalWidth() - 100, 20))
+ local progress = 1 - loadingscreen.GetProgress() / 100.0
+ loadingbarparams.SetMaskAlphaRange(math.saturate(progress - 0.05), math.saturate(progress))
+ loadingbar.SetParams(loadingbarparams)
+ end
+
+ -- After loading finished, we clear the main scene, and merge loaded scene into it:
+ scene.Clear()
+ scene.Merge(loading_scene)
+ scene.Update(0)
+
+ scene.VoxelizeScene(voxelgrid, false, FILTER_NAVIGATION_MESH | FILTER_COLLIDER, ~(Layers.Player | Layers.NPC)) -- player and npc layers not included in voxelization
+
+ -- Parse animations from anim_scene, which was loaded by the loading screen:
+ LoadAnimations(anim_scene)
+
+ -- Create characters from root Entity handles that were loaded by loading screen
+ local player = Character(character_entities[1], Vector(0,0.5,0), Vector(0,0,1), true, anim_scene)
+ local npcs = {
+ -- Patrolling NPC IDs: 1,2,3
+ Character(character_entities[2], Vector(4,0.1,4), Vector(0,0,-1), false, anim_scene),
+ Character(character_entities[3], Vector(-8,1,4), Vector(-1,0,0), false, anim_scene),
+ Character(character_entities[4], Vector(-2,0.1,8), Vector(-1,0,0), false, anim_scene),
+
+ -- stationary NPC IDs: 3,4....
+ Character(character_entities[5], Vector(-1,0.1,-6), Vector(0,0,1), false, anim_scene),
+ --Character(character_entities[6], Vector(10.8,0.1,4.1), Vector(0,0,-1), false, anim_scene),
+ --Character(character_entities[7], Vector(11.1,4,7.2), Vector(-1,0,0), false, anim_scene),
+ }
+
+ local camera = ThirdPersonCamera(player)
+
path.AddFont(conversation.font)
path.AddFont(conversation.advance_font)
+
+ local help_text = "Wicked Engine Character demo (LUA)\n\n"
+ help_text = help_text .. "Controls:\n"
+ help_text = help_text .. "#############\n"
+ help_text = help_text .. "WASD/arrows/left analog stick: walk\n"
+ help_text = help_text .. "SHIFT/right shoulder button: walk -> jog\n"
+ help_text = help_text .. "E/left shoulder button: jog -> run\n"
+ help_text = help_text .. "SPACE/gamepad X/gamepad button 3: Jump\n"
+ help_text = help_text .. "Right Mouse Button/Right thumbstick: rotate camera\n"
+ help_text = help_text .. "Scoll middle mouse/Left-Right triggers: adjust camera distance\n"
+ help_text = help_text .. "ESCAPE key: quit\n"
+ help_text = help_text .. "ENTER key: interact\n"
+ help_text = help_text .. "R: reload script\n"
+ help_text = help_text .. "H: toggle debug draw\n"
+ help_text = help_text .. "L: toggle framerate lock\n"
+
+ -- Conversation dialogs:
+ local dialogtree = {
+ -- Dialog starts here:
+ {"Hello! Today is a nice day for a walk, isn't it? The sun is shining, the wind blows lightly, and the temperature is just perfect! To be honest, I don't need anything else to be happy."},
+ {"I just finished my morning routine and I'm ready for the day. What should I do now...?"},
+ {
+ "Anything I can do for you?",
+ choices = {
+ {
+ "Follow me!",
+ action = function()
+ conversation.character:Follow(player)
+ conversation.character.next_dialog = 4
+ end
+ },
+ {
+ "Never mind.",
+ action = function()
+ conversation.character.next_dialog = 5
+ end
+ }
+ }
+ },
+
+ -- Dialog 4: When chosen [Follow me] or [Just keep following me]
+ {"Lead the way!", action_after = function() conversation:Exit() conversation.character.next_dialog = 6 end},
+
+ -- Dialog 5: When chosen [Never mind] - this also modifies mood (expression) and state (anim) while dialog is playing
+ {
+ "Have a nice day!",
+ action = function()
+ conversation.character.mood = Mood.Happy
+ conversation.character.state = States.WAVE
+ conversation.character.anim_amount = 0.1
+ end,
+ action_after = function()
+ conversation.character.mood = Mood.Neutral
+ conversation.character.state = States.IDLE
+ conversation.character.anim_amount = 1
+ conversation:Exit()
+ conversation.character.next_dialog = 1
+ end
+ },
+
+ -- Dialog 6: After Dialog 4 finished, so character is following player
+ {
+ "Where are we going?",
+ choices = {
+ {"Just keep following me.", action = function() conversation.character.next_dialog = 4 end},
+ {"Stay here!", action = function() conversation.character:Unfollow() end}
+ }
+ },
+ {"Gotcha!"}, -- After chosen [Stay here]
+ }
+
+ for i,npc in pairs(npcs) do
+ npc.dialogs = dialogtree
+ end
+
+ -- Patrol waypoints:
+
+ local waypoints = {
+ scene.Entity_FindByName("waypoint1"),
+ scene.Entity_FindByName("waypoint2"),
+
+ scene.Entity_FindByName("waypoint3"),
+ scene.Entity_FindByName("waypoint4"),
+ scene.Entity_FindByName("waypoint5"),
+ scene.Entity_FindByName("waypoint6"),
+
+ scene.Entity_FindByName("waypoint7"),
+ scene.Entity_FindByName("waypoint8"),
+ scene.Entity_FindByName("waypoint9"),
+ scene.Entity_FindByName("waypoint10"),
+ scene.Entity_FindByName("waypoint11"),
+ scene.Entity_FindByName("waypoint12"),
+ scene.Entity_FindByName("waypoint13"),
+ scene.Entity_FindByName("waypoint14"),
+ scene.Entity_FindByName("waypoint15"),
+ scene.Entity_FindByName("waypoint16"),
+ scene.Entity_FindByName("waypoint17"),
+ scene.Entity_FindByName("waypoint18"),
+ scene.Entity_FindByName("waypoint19"),
+ scene.Entity_FindByName("waypoint20"),
+ scene.Entity_FindByName("waypoint21"),
+ scene.Entity_FindByName("waypoint22"),
+ scene.Entity_FindByName("waypoint23"),
+ scene.Entity_FindByName("waypoint24"),
+ scene.Entity_FindByName("waypoint25"),
+ }
+
+ -- Simplest 1-2 patrol:
+ if(
+ waypoints[1] ~= INVALID_ENTITY and
+ waypoints[2] ~= INVALID_ENTITY
+ ) then
+ npcs[1].patrol_waypoints = {
+ {
+ entity = waypoints[1],
+ wait = 0,
+ },
+ {
+ entity = waypoints[2],
+ wait = 2,
+ },
+ }
+ end
+
+ -- Some more advanced, toggle between walk and jog, also swimming (because waypoints are across water mesh in test level):
+ if(
+ waypoints[3] ~= INVALID_ENTITY and
+ waypoints[4] ~= INVALID_ENTITY and
+ waypoints[5] ~= INVALID_ENTITY and
+ waypoints[6] ~= INVALID_ENTITY
+ ) then
+ npcs[2].patrol_waypoints = {
+ {
+ entity = waypoints[3],
+ wait = 0,
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[4],
+ wait = 0,
+ },
+ {
+ entity = waypoints[5],
+ wait = 2,
+ },
+ {
+ entity = waypoints[6],
+ wait = 0,
+ state = States.JOG,
+ },
+ }
+ end
+
+
+ -- Run long circle:
+ if(
+ waypoints[7] ~= INVALID_ENTITY and
+ waypoints[8] ~= INVALID_ENTITY and
+ waypoints[9] ~= INVALID_ENTITY and
+ waypoints[10] ~= INVALID_ENTITY and
+ waypoints[11] ~= INVALID_ENTITY and
+ waypoints[12] ~= INVALID_ENTITY and
+ waypoints[13] ~= INVALID_ENTITY and
+ waypoints[14] ~= INVALID_ENTITY and
+ waypoints[15] ~= INVALID_ENTITY and
+ waypoints[16] ~= INVALID_ENTITY and
+ waypoints[17] ~= INVALID_ENTITY and
+ waypoints[18] ~= INVALID_ENTITY and
+ waypoints[19] ~= INVALID_ENTITY and
+ waypoints[20] ~= INVALID_ENTITY and
+ waypoints[21] ~= INVALID_ENTITY and
+ waypoints[22] ~= INVALID_ENTITY and
+ waypoints[23] ~= INVALID_ENTITY and
+ waypoints[24] ~= INVALID_ENTITY and
+ waypoints[25] ~= INVALID_ENTITY
+ ) then
+ npcs[3].patrol_waypoints = {
+ {
+ entity = waypoints[7],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[8],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[9],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[10],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[11],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[12],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[13],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[14],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[15],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[16],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[17],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[18],
+ state = States.JOG,
+ wait = 2, -- little wait at top of slope
+ },
+ {
+ entity = waypoints[19],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[20],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[21],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[22],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[23],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[24],
+ state = States.JOG,
+ },
+ {
+ entity = waypoints[25],
+ state = States.JOG,
+ },
+ }
+ end
+
+ -- Main loop:
while true do
player:Update()
@@ -1262,58 +1560,7 @@ runProcess(function()
scene.VoxelizeScene(voxelgrid, false, FILTER_NAVIGATION_MESH | FILTER_COLLIDER, ~(Layers.Player | Layers.NPC)) -- player and npc layers not included in voxelization
end
- update()
- if not backlog_isactive() then
- if(input.Press(KEYBOARD_BUTTON_ESCAPE)) then
- -- restore previous component
- -- so if you loaded this script from the editor, you can go back to the editor with ESC
- backlog_post("EXIT")
- killProcesses()
- application.SetActivePath(prevPath)
- return
- end
- if(input.Press(string.byte('R'))) then
- -- reload script
- backlog_post("RELOAD")
- killProcesses()
- application.SetActivePath(prevPath)
- dofile(script_file())
- return
- end
- if input.Press(string.byte('L')) then
- framerate_lock = not framerate_lock
- application.SetFrameRateLock(framerate_lock)
- if framerate_lock then
- application.SetTargetFrameRate(framerate_lock_target)
- end
- end
- end
-
- end
-
-end)
-
--- Draw
-runProcess(function()
-
- local help_text = "Wicked Engine Character demo (LUA)\n\n"
- help_text = help_text .. "Controls:\n"
- help_text = help_text .. "#############\n"
- help_text = help_text .. "WASD/arrows/left analog stick: walk\n"
- help_text = help_text .. "SHIFT/right shoulder button: walk -> jog\n"
- help_text = help_text .. "E/left shoulder button: jog -> run\n"
- help_text = help_text .. "SPACE/gamepad X/gamepad button 3: Jump\n"
- help_text = help_text .. "Right Mouse Button/Right thumbstick: rotate camera\n"
- help_text = help_text .. "Scoll middle mouse/Left-Right triggers: adjust camera distance\n"
- help_text = help_text .. "ESCAPE key: quit\n"
- help_text = help_text .. "ENTER key: interact\n"
- help_text = help_text .. "R: reload script\n"
- help_text = help_text .. "H: toggle debug draw\n"
- help_text = help_text .. "L: toggle framerate lock\n"
-
- while true do
-
-- Do some debug draw geometry:
DrawDebugText(help_text, Vector(0,2,2), Vector(1,1,1,1), 0.1, DEBUG_TEXT_DEPTH_TEST)
@@ -1362,249 +1609,37 @@ runProcess(function()
end
- -- Wait for the engine to render the scene
- render()
- end
-end)
-
--- Conversation dialogs:
-local dialogtree = {
- -- Dialog starts here:
- {"Hello! Today is a nice day for a walk, isn't it? The sun is shining, the wind blows lightly, and the temperature is just perfect! To be honest, I don't need anything else to be happy."},
- {"I just finished my morning routine and I'm ready for the day. What should I do now...?"},
- {
- "Anything I can do for you?",
- choices = {
- {
- "Follow me!",
- action = function()
- conversation.character:Follow(player)
- conversation.character.next_dialog = 4
+ update()
+
+ if not backlog_isactive() then
+ if(input.Press(KEYBOARD_BUTTON_ESCAPE)) then
+ -- restore previous component
+ -- so if you loaded this script from the editor, you can go back to the editor with ESC
+ backlog_post("EXIT")
+ application.SetActivePath(prevPath)
+ killProcesses()
+ return
+ end
+ if(input.Press(string.byte('R'))) then
+ -- reload script
+ backlog_post("RELOAD")
+ application.SetActivePath(prevPath, 0.5)
+ while not application.IsFaded() do
+ update()
end
- },
- {
- "Never mind.",
- action = function()
- conversation.character.next_dialog = 5
+ killProcesses()
+ dofile(script_file())
+ return
+ end
+ if input.Press(string.byte('L')) then
+ framerate_lock = not framerate_lock
+ application.SetFrameRateLock(framerate_lock)
+ if framerate_lock then
+ application.SetTargetFrameRate(framerate_lock_target)
end
- }
- }
- },
-
- -- Dialog 4: When chosen [Follow me] or [Just keep following me]
- {"Lead the way!", action_after = function() conversation:Exit() conversation.character.next_dialog = 6 end},
-
- -- Dialog 5: When chosen [Never mind] - this also modifies mood (expression) and state (anim) while dialog is playing
- {
- "Have a nice day!",
- action = function()
- conversation.character.mood = Mood.Happy
- conversation.character.state = States.WAVE
- conversation.character.anim_amount = 0.1
- end,
- action_after = function()
- conversation.character.mood = Mood.Neutral
- conversation.character.state = States.IDLE
- conversation.character.anim_amount = 1
- conversation:Exit()
- conversation.character.next_dialog = 1
+ end
end
- },
-
- -- Dialog 6: After Dialog 4 finished, so character is following player
- {
- "Where are we going?",
- choices = {
- {"Just keep following me.", action = function() conversation.character.next_dialog = 4 end},
- {"Stay here!", action = function() conversation.character:Unfollow() end}
- }
- },
- {"Gotcha!"}, -- After chosen [Stay here]
-}
-
-for i,npc in pairs(npcs) do
- npc.dialogs = dialogtree
-end
-
--- Patrol waypoints:
-
-local waypoints = {
- scene.Entity_FindByName("waypoint1"),
- scene.Entity_FindByName("waypoint2"),
-
- scene.Entity_FindByName("waypoint3"),
- scene.Entity_FindByName("waypoint4"),
- scene.Entity_FindByName("waypoint5"),
- scene.Entity_FindByName("waypoint6"),
-
- scene.Entity_FindByName("waypoint7"),
- scene.Entity_FindByName("waypoint8"),
- scene.Entity_FindByName("waypoint9"),
- scene.Entity_FindByName("waypoint10"),
- scene.Entity_FindByName("waypoint11"),
- scene.Entity_FindByName("waypoint12"),
- scene.Entity_FindByName("waypoint13"),
- scene.Entity_FindByName("waypoint14"),
- scene.Entity_FindByName("waypoint15"),
- scene.Entity_FindByName("waypoint16"),
- scene.Entity_FindByName("waypoint17"),
- scene.Entity_FindByName("waypoint18"),
- scene.Entity_FindByName("waypoint19"),
- scene.Entity_FindByName("waypoint20"),
- scene.Entity_FindByName("waypoint21"),
- scene.Entity_FindByName("waypoint22"),
- scene.Entity_FindByName("waypoint23"),
- scene.Entity_FindByName("waypoint24"),
- scene.Entity_FindByName("waypoint25"),
-}
-
--- Simplest 1-2 patrol:
-if(
- waypoints[1] ~= INVALID_ENTITY and
- waypoints[2] ~= INVALID_ENTITY
-) then
- npcs[1].patrol_waypoints = {
- {
- entity = waypoints[1],
- wait = 0,
- },
- {
- entity = waypoints[2],
- wait = 2,
- },
- }
-end
-
--- Some more advanced, toggle between walk and jog, also swimming (because waypoints are across water mesh in test level):
-if(
- waypoints[3] ~= INVALID_ENTITY and
- waypoints[4] ~= INVALID_ENTITY and
- waypoints[5] ~= INVALID_ENTITY and
- waypoints[6] ~= INVALID_ENTITY
-) then
- npcs[2].patrol_waypoints = {
- {
- entity = waypoints[3],
- wait = 0,
- state = States.JOG,
- },
- {
- entity = waypoints[4],
- wait = 0,
- },
- {
- entity = waypoints[5],
- wait = 2,
- },
- {
- entity = waypoints[6],
- wait = 0,
- state = States.JOG,
- },
- }
-end
-
-
--- Run long circle:
-if(
- waypoints[7] ~= INVALID_ENTITY and
- waypoints[8] ~= INVALID_ENTITY and
- waypoints[9] ~= INVALID_ENTITY and
- waypoints[10] ~= INVALID_ENTITY and
- waypoints[11] ~= INVALID_ENTITY and
- waypoints[12] ~= INVALID_ENTITY and
- waypoints[13] ~= INVALID_ENTITY and
- waypoints[14] ~= INVALID_ENTITY and
- waypoints[15] ~= INVALID_ENTITY and
- waypoints[16] ~= INVALID_ENTITY and
- waypoints[17] ~= INVALID_ENTITY and
- waypoints[18] ~= INVALID_ENTITY and
- waypoints[19] ~= INVALID_ENTITY and
- waypoints[20] ~= INVALID_ENTITY and
- waypoints[21] ~= INVALID_ENTITY and
- waypoints[22] ~= INVALID_ENTITY and
- waypoints[23] ~= INVALID_ENTITY and
- waypoints[24] ~= INVALID_ENTITY and
- waypoints[25] ~= INVALID_ENTITY
-) then
- npcs[3].patrol_waypoints = {
- {
- entity = waypoints[7],
- state = States.JOG,
- },
- {
- entity = waypoints[8],
- state = States.JOG,
- },
- {
- entity = waypoints[9],
- state = States.JOG,
- },
- {
- entity = waypoints[10],
- state = States.JOG,
- },
- {
- entity = waypoints[11],
- state = States.JOG,
- },
- {
- entity = waypoints[12],
- state = States.JOG,
- },
- {
- entity = waypoints[13],
- state = States.JOG,
- },
- {
- entity = waypoints[14],
- state = States.JOG,
- },
- {
- entity = waypoints[15],
- state = States.JOG,
- },
- {
- entity = waypoints[16],
- state = States.JOG,
- },
- {
- entity = waypoints[17],
- state = States.JOG,
- },
- {
- entity = waypoints[18],
- state = States.JOG,
- wait = 2, -- little wait at top of slope
- },
- {
- entity = waypoints[19],
- state = States.JOG,
- },
- {
- entity = waypoints[20],
- state = States.JOG,
- },
- {
- entity = waypoints[21],
- state = States.JOG,
- },
- {
- entity = waypoints[22],
- state = States.JOG,
- },
- {
- entity = waypoints[23],
- state = States.JOG,
- },
- {
- entity = waypoints[24],
- state = States.JOG,
- },
- {
- entity = waypoints[25],
- state = States.JOG,
- },
- }
-end
+ end
+
+end)
diff --git a/WickedEngine/wiApplication.cpp b/WickedEngine/wiApplication.cpp
index 0474c7ad7..155376319 100644
--- a/WickedEngine/wiApplication.cpp
+++ b/WickedEngine/wiApplication.cpp
@@ -60,7 +60,6 @@ namespace wi
}
// Fade manager will activate on fadeout
- fadeManager.Clear();
fadeManager.Start(fadeSeconds, fadeColor, [this, component]() {
if (GetActivePath() != nullptr)
@@ -109,22 +108,6 @@ namespace wi
return;
}
-#if 0
-#ifdef WICKEDENGINE_BUILD_DX12
- static bool startup_workaround = false;
- if (!startup_workaround)
- {
- startup_workaround = true;
- if (dynamic_cast(graphicsDevice.get()))
- {
- CommandList cmd = graphicsDevice->BeginCommandList();
- wi::renderer::Workaround(1, cmd);
- graphicsDevice->SubmitCommandLists();
- }
- }
-#endif // WICKEDENGINE_BUILD_DX12
-#endif
-
static bool startup_script = false;
if (!startup_script)
{
diff --git a/WickedEngine/wiApplication.h b/WickedEngine/wiApplication.h
index 88f8ae030..4ce4da288 100644
--- a/WickedEngine/wiApplication.h
+++ b/WickedEngine/wiApplication.h
@@ -120,6 +120,8 @@ namespace wi
// display all-time engine information text
InfoDisplayer infoDisplay;
+ bool IsFaded() const { return fadeManager.IsFaded(); }
+
};
}
diff --git a/WickedEngine/wiApplication_BindLua.cpp b/WickedEngine/wiApplication_BindLua.cpp
index 7faa90eed..5b60d7b36 100644
--- a/WickedEngine/wiApplication_BindLua.cpp
+++ b/WickedEngine/wiApplication_BindLua.cpp
@@ -28,6 +28,7 @@ namespace wi::lua
lunamethod(Application_BindLua, GetCanvas),
lunamethod(Application_BindLua, SetCanvas),
lunamethod(Application_BindLua, Exit),
+ lunamethod(Application_BindLua, IsFaded),
{ NULL, NULL }
};
Luna::PropertyType Application_BindLua::properties[] = {
@@ -389,6 +390,16 @@ namespace wi::lua
wi::platform::Exit();
return 0;
}
+ int Application_BindLua::IsFaded(lua_State* L)
+ {
+ if (component == nullptr)
+ {
+ wi::lua::SError(L, "IsFaded() component is empty!");
+ return 0;
+ }
+ wi::lua::SSetBool(L, component->IsFaded());
+ return 1;
+ }
int SetProfilerEnabled(lua_State* L)
diff --git a/WickedEngine/wiApplication_BindLua.h b/WickedEngine/wiApplication_BindLua.h
index 03af22c97..fecc91483 100644
--- a/WickedEngine/wiApplication_BindLua.h
+++ b/WickedEngine/wiApplication_BindLua.h
@@ -31,9 +31,8 @@ namespace wi::lua
class Application_BindLua
{
- private:
- Application* component = nullptr;
public:
+ Application* component = nullptr;
inline static constexpr char className[] = "Application";
static Luna::FunctionType methods[];
static Luna::PropertyType properties[];
@@ -60,6 +59,7 @@ namespace wi::lua
int SetCanvas(lua_State* L);
int Exit(lua_State* L);
+ int IsFaded(lua_State* L);
static void Bind();
};
diff --git a/WickedEngine/wiFadeManager.cpp b/WickedEngine/wiFadeManager.cpp
index 34d6f0bda..4469537e9 100644
--- a/WickedEngine/wiFadeManager.cpp
+++ b/WickedEngine/wiFadeManager.cpp
@@ -11,6 +11,7 @@ namespace wi
void FadeManager::Update(float dt)
{
+ fadeEventTriggeredThisFrame = false;
if (!IsActive())
return;
@@ -19,6 +20,7 @@ namespace wi
// skip fade, just launch the job
onFade();
state = FADE_FINISHED;
+ fadeEventTriggeredThisFrame = true;
}
float t = timer / targetFadeTimeInSeconds;
@@ -39,6 +41,7 @@ namespace wi
opacity = 1.0f;
onFade();
timer = 0;
+ fadeEventTriggeredThisFrame = true;
}
else if (state == FADE_OUT)
{
diff --git a/WickedEngine/wiFadeManager.h b/WickedEngine/wiFadeManager.h
index 70fe13e0c..d5e7757d6 100644
--- a/WickedEngine/wiFadeManager.h
+++ b/WickedEngine/wiFadeManager.h
@@ -23,6 +23,7 @@ namespace wi
} state = FADE_FINISHED;
wi::Color color = wi::Color(0, 0, 0, 255);
std::function onFade = [] {};
+ bool fadeEventTriggeredThisFrame = false;
FadeManager()
{
@@ -34,15 +35,23 @@ namespace wi
targetFadeTimeInSeconds = seconds;
this->color = color;
timer = 0;
- state = FADE_IN;
+ if (IsFaded())
+ {
+ // If starting a new fade on mid-fadeout, it will start from faded and just transition out of mid
+ state = FADE_MID;
+ }
+ else
+ {
+ state = FADE_IN;
+ }
onFade = onFadeFunction;
}
void Update(float dt);
- bool IsFaded()
+ bool IsFaded() const
{
- return state == FADE_MID;
+ return fadeEventTriggeredThisFrame;
}
- bool IsActive()
+ bool IsActive() const
{
return state != FADE_FINISHED;
}
diff --git a/WickedEngine/wiImageParams_BindLua.cpp b/WickedEngine/wiImageParams_BindLua.cpp
index a1035d16f..35acef66d 100644
--- a/WickedEngine/wiImageParams_BindLua.cpp
+++ b/WickedEngine/wiImageParams_BindLua.cpp
@@ -45,6 +45,8 @@ namespace wi::lua
lunamethod(ImageParams_BindLua, DisableBackgroundBlur),
lunamethod(ImageParams_BindLua, EnableBackground),
lunamethod(ImageParams_BindLua, DisableBackground),
+ lunamethod(ImageParams_BindLua, SetMaskAlphaRange),
+ lunamethod(ImageParams_BindLua, GetMaskAlphaRange),
{ nullptr, nullptr }
};
Luna::PropertyType ImageParams_BindLua::properties[] = {
@@ -431,6 +433,24 @@ namespace wi::lua
params.disableBackground();
return 0;
}
+ int ImageParams_BindLua::SetMaskAlphaRange(lua_State* L)
+ {
+ int argc = wi::lua::SGetArgCount(L);
+ if (argc < 2)
+ {
+ wi::lua::SError(L, "SetMaskAlphaRange(float start, end): not enough arguments!");
+ return 0;
+ }
+ params.mask_alpha_range_start = wi::lua::SGetFloat(L, 1);
+ params.mask_alpha_range_end = wi::lua::SGetFloat(L, 2);
+ return 0;
+ }
+ int ImageParams_BindLua::GetMaskAlphaRange(lua_State* L)
+ {
+ wi::lua::SSetFloat(L, params.mask_alpha_range_start);
+ wi::lua::SSetFloat(L, params.mask_alpha_range_end);
+ return 2;
+ }
ImageParams_BindLua::ImageParams_BindLua(lua_State* L)
{
diff --git a/WickedEngine/wiImageParams_BindLua.h b/WickedEngine/wiImageParams_BindLua.h
index 3cea747c7..7c415f910 100644
--- a/WickedEngine/wiImageParams_BindLua.h
+++ b/WickedEngine/wiImageParams_BindLua.h
@@ -58,6 +58,8 @@ namespace wi::lua
int DisableBackgroundBlur(lua_State* L);
int EnableBackground(lua_State* L);
int DisableBackground(lua_State* L);
+ int SetMaskAlphaRange(lua_State* L);
+ int GetMaskAlphaRange(lua_State* L);
static void Bind();
};
diff --git a/WickedEngine/wiLoadingScreen.cpp b/WickedEngine/wiLoadingScreen.cpp
index c201ef578..00d649617 100644
--- a/WickedEngine/wiLoadingScreen.cpp
+++ b/WickedEngine/wiLoadingScreen.cpp
@@ -1,16 +1,33 @@
#include "wiLoadingScreen.h"
#include "wiApplication.h"
+#include "wiEventHandler.h"
#include
+using namespace wi::graphics;
+
namespace wi
{
- bool LoadingScreen::isActive()
+ bool LoadingScreen::isActive() const
{
return wi::jobsystem::IsBusy(ctx);
}
+ bool LoadingScreen::isFinished() const
+ {
+ return tasks.empty();
+ }
+
+ int LoadingScreen::getProgress() const
+ {
+ if (launchedTasks == 0)
+ return 100;
+ uint32_t counter = ctx.counter.load();
+ float percent = 1 - float(counter) / float(launchedTasks);
+ return (int)std::round(percent * 100);
+ }
+
void LoadingScreen::addLoadingFunction(std::function loadingFunction)
{
if (loadingFunction != nullptr)
@@ -37,24 +54,42 @@ namespace wi
void LoadingScreen::Start()
{
+ launchedTasks = (uint32_t)tasks.size();
for (auto& x : tasks)
{
wi::jobsystem::Execute(ctx, x);
}
std::thread([this]() {
wi::jobsystem::Wait(ctx);
- finish();
- }).detach();
+ wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [this](uint64_t) {
+ if (finish != nullptr)
+ finish();
+ tasks.clear();
+ launchedTasks = 0;
+ finish = nullptr;
+ });
+ }).detach();
- RenderPath2D::Start();
+ RenderPath2D::Start();
}
- void LoadingScreen::Stop()
+ void LoadingScreen::Compose(wi::graphics::CommandList cmd) const
{
- tasks.clear();
- finish = nullptr;
+ if (backgroundTexture.IsValid())
+ {
+ wi::image::Params fx;
+ fx.enableFullScreen();
+ fx.blendFlag = wi::enums::BLENDMODE_PREMULTIPLIED;
+ if (colorspace != ColorSpace::SRGB)
+ {
+ // Convert the regular SRGB result of the render path to linear space for HDR compositing:
+ fx.enableLinearOutputMapping(hdr_scaling);
+ }
+ const Texture& tex = backgroundTexture.GetTexture();
+ wi::image::Draw(&tex, fx, cmd);
+ }
- RenderPath2D::Stop();
+ RenderPath2D::Compose(cmd);
}
}
diff --git a/WickedEngine/wiLoadingScreen.h b/WickedEngine/wiLoadingScreen.h
index 647b2ce68..775259000 100644
--- a/WickedEngine/wiLoadingScreen.h
+++ b/WickedEngine/wiLoadingScreen.h
@@ -14,27 +14,31 @@ namespace wi
class LoadingScreen :
public RenderPath2D
{
- private:
+ protected:
wi::jobsystem::context ctx;
wi::vector> tasks;
std::function finish;
+ uint32_t launchedTasks = 0;
public:
+ wi::Resource backgroundTexture;
//Add a loading task which should be executed
- //use std::bind( YourFunctionPointer )
void addLoadingFunction(std::function loadingFunction);
//Helper for loading a whole renderable component
void addLoadingComponent(RenderPath* component, Application* main, float fadeSeconds = 0, wi::Color fadeColor = wi::Color(0, 0, 0, 255));
//Set a function that should be called when the loading finishes
- //use std::bind( YourFunctionPointer )
void onFinished(std::function finishFunction);
//See if the loading is currently running
- bool isActive();
+ bool isActive() const;
+ // See if there are any loading tasks that are still not finished
+ bool isFinished() const;
+ // Returns the percentage of loading tasks that are finished (0% - 100%)
+ int getProgress() const;
//Start Executing the tasks and mark the loading as active
- virtual void Start() override;
- //Clear all tasks
- virtual void Stop() override;
+ void Start() override;
+
+ void Compose(wi::graphics::CommandList cmd) const override;
};
}
diff --git a/WickedEngine/wiLoadingScreen_BindLua.cpp b/WickedEngine/wiLoadingScreen_BindLua.cpp
index a0104b983..479167c7d 100644
--- a/WickedEngine/wiLoadingScreen_BindLua.cpp
+++ b/WickedEngine/wiLoadingScreen_BindLua.cpp
@@ -1,4 +1,14 @@
#include "wiLoadingScreen_BindLua.h"
+#include "wiScene_BindLua.h"
+#include "wiTexture_BindLua.h"
+#include "wiApplication_BindLua.h"
+#include "wiRenderPath3D_BindLua.h"
+
+#include
+
+using namespace wi::lua::scene;
+using namespace wi::scene;
+using namespace wi::ecs;
namespace wi::lua
{
@@ -21,54 +31,246 @@ namespace wi::lua
lunamethod(RenderPath_BindLua, GetLayerMask),
lunamethod(RenderPath_BindLua, SetLayerMask),
- lunamethod(LoadingScreen_BindLua, AddLoadingTask),
- lunamethod(LoadingScreen_BindLua, OnFinished),
+ lunamethod(LoadingScreen_BindLua, AddLoadModelTask),
+ lunamethod(LoadingScreen_BindLua, AddRenderPathActivationTask),
+ lunamethod(LoadingScreen_BindLua, IsFinished),
+ lunamethod(LoadingScreen_BindLua, GetProgress),
+ lunamethod(LoadingScreen_BindLua, SetBackgroundTexture),
+ lunamethod(LoadingScreen_BindLua, GetBackgroundTexture),
{ NULL, NULL }
};
Luna::PropertyType LoadingScreen_BindLua::properties[] = {
{ NULL, NULL }
};
- int LoadingScreen_BindLua::AddLoadingTask(lua_State* L)
+ int LoadingScreen_BindLua::AddLoadModelTask(lua_State* L)
{
+ LoadingScreen* loading = dynamic_cast(component);
+ if (loading == nullptr)
+ {
+ wi::lua::SError(L, "AddLoadModelTask(Scene scene, string fileName, opt Matrix transform): loading screen is invalid!");
+ return 0;
+ }
+
int argc = wi::lua::SGetArgCount(L);
if (argc > 0)
{
- std::string task = wi::lua::SGetString(L, 1);
- LoadingScreen* loading = dynamic_cast(component);
- if (loading != nullptr)
+ Scene_BindLua* custom_scene = Luna::lightcheck(L, 1);
+ if (custom_scene)
{
- loading->addLoadingFunction([=](wi::jobsystem::JobArgs args) {
- wi::lua::RunText(task);
+ // Overload 1: thread safe version
+ if (argc > 1)
+ {
+ std::string fileName = wi::lua::SGetString(L, 2);
+ XMMATRIX transform = XMMatrixIdentity();
+ if (argc > 2)
+ {
+ Matrix_BindLua* matrix = Luna::lightcheck(L, 3);
+ if (matrix != nullptr)
+ {
+ transform = XMLoadFloat4x4(&matrix->data);
+ }
+ else
+ {
+ wi::lua::SError(L, "AddLoadModelTask(Scene scene, string fileName, opt Matrix transform) argument is not a matrix!");
+ }
+ }
+ Entity root = CreateEntity();
+ loading->addLoadingFunction([=](wi::jobsystem::JobArgs args) {
+ Scene scene;
+ wi::scene::LoadModel2(scene, fileName, transform, root);
+
+ // Note: we lock the scene for merging from multiple loading screen tasks
+ // Because we don't have fine control over thread execution in lua, and this significantly
+ // simplifies loading into scene from loadingscreen
+ std::scoped_lock lck(custom_scene->scene->locker);
+ custom_scene->scene->Merge(scene);
});
+ wi::lua::SSetLongLong(L, root);
+ return 1;
+ }
+ else
+ {
+ wi::lua::SError(L, "AddLoadModelTask(Scene scene, string fileName, opt Matrix transform) not enough arguments!");
+ return 0;
+ }
}
else
- wi::lua::SError(L, "AddLoader(string taskScript) component is not a LoadingScreen!");
+ {
+ // Overload 2: global scene version
+ std::string fileName = wi::lua::SGetString(L, 1);
+ XMMATRIX transform = XMMatrixIdentity();
+ if (argc > 1)
+ {
+ Matrix_BindLua* matrix = Luna::lightcheck(L, 2);
+ if (matrix != nullptr)
+ {
+ transform = XMLoadFloat4x4(&matrix->data);
+ }
+ else
+ {
+ wi::lua::SError(L, "AddLoadModelTask(string fileName, opt Matrix transform) argument is not a matrix!");
+ }
+ }
+ Entity root = CreateEntity();
+ loading->addLoadingFunction([=](wi::jobsystem::JobArgs args) {
+ Scene scene;
+ wi::scene::LoadModel2(scene, fileName, transform, root);
+
+ // Note: we lock the scene for merging from multiple loading screen tasks
+ // Because we don't have fine control over thread execution in lua, and this significantly
+ // simplifies loading into scene from loadingscreen
+ std::scoped_lock lck(GetGlobalScene()->locker);
+ GetGlobalScene()->Merge(scene);
+ });
+ wi::lua::SSetLongLong(L, root);
+ return 1;
+ }
}
else
- wi::lua::SError(L, "AddLoader(string taskScript) not enough arguments!");
+ {
+ wi::lua::SError(L, "AddLoadModelTask(string fileName, opt Matrix transform) not enough arguments!");
+ }
return 0;
}
- int LoadingScreen_BindLua::OnFinished(lua_State* L)
+ int LoadingScreen_BindLua::AddRenderPathActivationTask(lua_State* L)
{
- int argc = wi::lua::SGetArgCount(L);
- if (argc > 0)
+ LoadingScreen* loading = dynamic_cast(component);
+ if (loading == nullptr)
{
- std::string task = wi::lua::SGetString(L, 1);
- LoadingScreen* loading = dynamic_cast(component);
- if (loading != nullptr)
+ wi::lua::SError(L, "AddRenderPathActivationTask(RenderPath path, opt float fadeSeconds = 0, opt int fadeR = 0,fadeG = 0,fadeB = 0): loading screen is invalid!");
+ return 0;
+ }
+
+ int argc = wi::lua::SGetArgCount(L);
+ if (argc < 2)
+ {
+ wi::lua::SError(L, "AddRenderPathActivationTask(RenderPath path, Application app, opt float fadeSeconds = 0, opt int fadeR = 0,fadeG = 0,fadeB = 0): not enough arguments!");
+ return 0;
+ }
+ RenderPath* path = nullptr;
+
+ RenderPath3D_BindLua* comp3D = Luna::lightcheck(L, 1);
+ if (comp3D == nullptr)
+ {
+ RenderPath2D_BindLua* comp2D = Luna::lightcheck(L, 1);
+ if (comp2D == nullptr)
{
- loading->onFinished([=] {
- wi::lua::RunText(task);
- });
+ LoadingScreen_BindLua* compLoading = Luna::lightcheck(L, 1);
+ if (compLoading == nullptr)
+ {
+ RenderPath_BindLua* comp = Luna::lightcheck(L, 1);
+ if (comp == nullptr)
+ {
+ wi::lua::SError(L, "AddRenderPathActivationTask(RenderPath path, Application app, opt float fadeSeconds = 0, opt int fadeR = 0,fadeG = 0,fadeB = 0): first argument is not a RenderPath!");
+ return 0;
+ }
+ else
+ {
+ path = comp->component;
+ }
+ }
+ else
+ {
+ path = compLoading->component;
+ }
}
else
- wi::lua::SError(L, "OnFinished(string taskScript) component is not a LoadingScreen!");
+ {
+ path = comp2D->component;
+ }
}
else
- wi::lua::SError(L, "OnFinished(string taskScript) not enough arguments!");
+ {
+ path = comp3D->component;
+ }
+
+ Application_BindLua* app = Luna::lightcheck(L, 2);
+ if (app == nullptr)
+ {
+ wi::lua::SError(L, "AddRenderPathActivationTask(RenderPath path, Application app, opt float fadeSeconds = 0, opt int fadeR = 0,fadeG = 0,fadeB = 0): second argument is not an Application!");
+ return 0;
+ }
+
+ float fadeSeconds = 0;
+ wi::Color fadeColor = wi::Color::Black();
+
+ if (argc > 2)
+ {
+ fadeSeconds = wi::lua::SGetFloat(L, 3);
+ if (argc > 3)
+ {
+ fadeColor.setR((uint8_t)wi::lua::SGetInt(L, 4));
+ if (argc > 4)
+ {
+ fadeColor.setG((uint8_t)wi::lua::SGetInt(L, 5));
+ if (argc > 5)
+ {
+ fadeColor.setB((uint8_t)wi::lua::SGetInt(L, 6));
+ }
+ }
+ }
+ }
+
+ loading->addLoadingComponent(path, app->component, fadeSeconds, fadeColor);
return 0;
}
+ int LoadingScreen_BindLua::IsFinished(lua_State* L)
+ {
+ LoadingScreen* loading = dynamic_cast(component);
+ if (loading != nullptr)
+ {
+ wi::lua::SSetBool(L, loading->isFinished());
+ return 1;
+ }
+ wi::lua::SError(L, "IsFinished(): loading screen is invalid!");
+ return 0;
+ }
+ int LoadingScreen_BindLua::GetProgress(lua_State* L)
+ {
+ LoadingScreen* loading = dynamic_cast(component);
+ if (loading != nullptr)
+ {
+ wi::lua::SSetInt(L, loading->getProgress());
+ return 1;
+ }
+ wi::lua::SError(L, "GetProgress(): loading screen is invalid!");
+ return 0;
+ }
+ int LoadingScreen_BindLua::SetBackgroundTexture(lua_State* L)
+ {
+ LoadingScreen* loading = dynamic_cast(component);
+ if (loading == nullptr)
+ {
+ wi::lua::SError(L, "SetBackgroundTexture(Texture tex): loading screen is not valid!");
+ return 0;
+ }
+ int argc = wi::lua::SGetArgCount(L);
+ if (argc < 1)
+ {
+ wi::lua::SError(L, "SetBackgroundTexture(Texture tex): not enough arguments!");
+ return 0;
+ }
+ Texture_BindLua* tex = Luna::lightcheck(L, 1);
+ if (tex == nullptr)
+ {
+ wi::lua::SError(L, "SetBackgroundTexture(Texture tex): argument is not a Texture!");
+ return 0;
+ }
+ loading->backgroundTexture = tex->resource;
+ return 0;
+ }
+ int LoadingScreen_BindLua::GetBackgroundTexture(lua_State* L)
+ {
+ LoadingScreen* loading = dynamic_cast(component);
+ if (loading == nullptr)
+ {
+ wi::lua::SError(L, "GetBackgroundTexture(): loading screen is not valid!");
+ return 0;
+ }
+ Luna::push(L, loading->backgroundTexture);
+ return 1;
+ }
void LoadingScreen_BindLua::Bind()
{
diff --git a/WickedEngine/wiLoadingScreen_BindLua.h b/WickedEngine/wiLoadingScreen_BindLua.h
index 925c11f6f..947cd9dee 100644
--- a/WickedEngine/wiLoadingScreen_BindLua.h
+++ b/WickedEngine/wiLoadingScreen_BindLua.h
@@ -25,8 +25,12 @@ namespace wi::lua
this->component = &loadingscreen;
}
- int AddLoadingTask(lua_State* L);
- int OnFinished(lua_State* L);
+ int AddLoadModelTask(lua_State* L);
+ int AddRenderPathActivationTask(lua_State* L);
+ int IsFinished(lua_State* L);
+ int GetProgress(lua_State* L);
+ int SetBackgroundTexture(lua_State* L);
+ int GetBackgroundTexture(lua_State* L);
static void Bind();
};
diff --git a/WickedEngine/wiRenderPath2D.h b/WickedEngine/wiRenderPath2D.h
index bc1c48341..a7fd5c9a7 100644
--- a/WickedEngine/wiRenderPath2D.h
+++ b/WickedEngine/wiRenderPath2D.h
@@ -13,7 +13,7 @@ namespace wi
class RenderPath2D :
public RenderPath
{
- private:
+ protected:
wi::graphics::Texture rtStenciled;
wi::graphics::Texture rtStenciled_resolved;
wi::graphics::Texture rtFinal;
diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp
index f9e07e02a..0817c5c27 100644
--- a/WickedEngine/wiScene.cpp
+++ b/WickedEngine/wiScene.cpp
@@ -4866,6 +4866,12 @@ namespace wi::scene
{
SoundComponent& sound = sounds[i];
+ if (!sound.soundinstance.IsValid())
+ {
+ sound.soundinstance.SetLooped(sound.IsLooped());
+ wi::audio::CreateSoundInstance(&sound.soundResource.GetSound(), &sound.soundinstance);
+ }
+
if (!sound.IsDisable3D())
{
Entity entity = sounds.GetEntity(i);
@@ -4917,6 +4923,8 @@ namespace wi::scene
}
void Scene::RunScriptUpdateSystem(wi::jobsystem::context& ctx)
{
+ if (dt == 0)
+ return; // not allowed to be run when dt == 0 as it could be on separate thread!
auto range = wi::profiler::BeginRangeCPU("Script Components");
for (size_t i = 0; i < scripts.GetCount(); ++i)
{
@@ -6285,57 +6293,78 @@ namespace wi::scene
Entity LoadModel(const std::string& fileName, const XMMATRIX& transformMatrix, bool attached)
{
- Scene scene;
- Entity root = LoadModel(scene, fileName, transformMatrix, attached);
- GetScene().Merge(scene);
- return root;
+ Entity rootEntity = INVALID_ENTITY;
+ if (attached)
+ {
+ rootEntity = CreateEntity();
+ }
+ LoadModel2(fileName, transformMatrix, rootEntity);
+ return rootEntity;
}
Entity LoadModel(Scene& scene, const std::string& fileName, const XMMATRIX& transformMatrix, bool attached)
{
- wi::Archive archive(fileName, true);
- if (archive.IsOpen())
+ Entity rootEntity = INVALID_ENTITY;
+ if (attached)
{
- // Serialize it from file:
- scene.Serialize(archive);
+ rootEntity = CreateEntity();
+ }
+ LoadModel2(scene, fileName, transformMatrix, rootEntity);
+ return rootEntity;
+ }
- // First, create new root:
- Entity root = CreateEntity();
- scene.transforms.Create(root);
- scene.layers.Create(root).layerMask = ~0;
+ void LoadModel2(const std::string& fileName, const XMMATRIX& transformMatrix, Entity rootEntity)
+ {
+ Scene scene;
+ LoadModel(scene, fileName, transformMatrix, rootEntity);
+ GetScene().Merge(scene);
+ }
+ void LoadModel2(Scene& scene, const std::string& fileName, const XMMATRIX& transformMatrix, Entity rootEntity)
+ {
+ wi::Archive archive(fileName, true);
+ if (!archive.IsOpen())
+ return;
+
+ // Serialize it from file:
+ scene.Serialize(archive);
+
+ // First, create new root:
+ bool attached = true;
+ if (rootEntity == INVALID_ENTITY)
+ {
+ rootEntity = CreateEntity();
+ attached = false;
+ }
+ scene.transforms.Create(rootEntity);
+ scene.layers.Create(rootEntity).layerMask = ~0;
+
+ {
+ // Apply the optional transformation matrix to the new scene:
+
+ // Parent all unparented transforms to new root entity
+ for (size_t i = 0; i < scene.transforms.GetCount(); ++i)
{
- // Apply the optional transformation matrix to the new scene:
-
- // Parent all unparented transforms to new root entity
- for (size_t i = 0; i < scene.transforms.GetCount() - 1; ++i) // GetCount() - 1 because the last added was the "root"
+ Entity entity = scene.transforms.GetEntity(i);
+ if (entity != rootEntity && !scene.hierarchy.Contains(entity))
{
- Entity entity = scene.transforms.GetEntity(i);
- if (!scene.hierarchy.Contains(entity))
- {
- scene.Component_Attach(entity, root);
- }
+ scene.Component_Attach(entity, rootEntity);
}
-
- // The root component is transformed, scene is updated:
- TransformComponent* root_transform = scene.transforms.GetComponent(root);
- root_transform->MatrixTransform(transformMatrix);
-
- scene.Update(0);
}
- if (!attached)
- {
- // In this case, we don't care about the root anymore, so delete it. This will simplify overall hierarchy
- scene.Component_DetachChildren(root);
- scene.Entity_Remove(root);
- root = INVALID_ENTITY;
- }
+ // The root component is transformed, scene is updated:
+ TransformComponent* root_transform = scene.transforms.GetComponent(rootEntity);
+ root_transform->MatrixTransform(transformMatrix);
- return root;
+ scene.Update(0);
}
- return INVALID_ENTITY;
+ if (!attached)
+ {
+ // In this case, we don't care about the root anymore, so delete it. This will simplify overall hierarchy
+ scene.Component_DetachChildren(rootEntity);
+ scene.Entity_Remove(rootEntity);
+ }
}
PickResult Pick(const wi::primitive::Ray& ray, uint32_t filterMask, uint32_t layerMask, const Scene& scene, uint32_t lod)
diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h
index 1db8f6bd0..167cb655e 100644
--- a/WickedEngine/wiScene.h
+++ b/WickedEngine/wiScene.h
@@ -546,6 +546,19 @@ namespace wi::scene
// returns INVALID_ENTITY if attached argument was false, else it returns the base entity handle
wi::ecs::Entity LoadModel(Scene& scene, const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), bool attached = false);
+ // Helper function to open a wiscene file and add the contents to the global scene
+ // fileName : file path
+ // transformMatrix : everything will be transformed by this matrix (optional)
+ // rootEntity : specify entity to attach whole scene to (optional)
+ void LoadModel2(const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), wi::ecs::Entity rootEntity = wi::ecs::INVALID_ENTITY);
+
+ // Helper function to open a wiscene file and add the contents to the specified scene. This is thread safe as it doesn't modify global scene
+ // scene : the scene that will contain the model
+ // fileName : file path
+ // transformMatrix : everything will be transformed by this matrix (optional)
+ // rootEntity : specify entity to attach whole scene to (optional)
+ void LoadModel2(Scene& scene, const std::string& fileName, const XMMATRIX& transformMatrix = XMMatrixIdentity(), wi::ecs::Entity rootEntity = wi::ecs::INVALID_ENTITY);
+
// Deprecated, use Scene::Intersects() function instead
using PickResult = Scene::RayIntersectionResult;
PickResult Pick(const wi::primitive::Ray& ray, uint32_t filterMask = wi::enums::FILTER_OPAQUE, uint32_t layerMask = ~0, const Scene& scene = GetScene(), uint32_t lod = 0);
diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp
index 8ffd93326..4068d8a76 100644
--- a/WickedEngine/wiScene_Serializers.cpp
+++ b/WickedEngine/wiScene_Serializers.cpp
@@ -1727,8 +1727,9 @@ namespace wi::scene
{
filename = dir + filename;
soundResource = wi::resourcemanager::Load(filename, wi::resourcemanager::Flags::IMPORT_RETAIN_FILEDATA);
- soundinstance.SetLooped(IsLooped());
- wi::audio::CreateSoundInstance(&soundResource.GetSound(), &soundinstance);
+ // Note: sound instance can't be created yet, as soundResource is not necessarily ready at this point
+ // Consider when multiple threads are loading the same sound, one thread will be loading the data,
+ // the others return early with the resource that will be containing the data once it has been loaded.
}
});
}
diff --git a/WickedEngine/wiSprite_BindLua.cpp b/WickedEngine/wiSprite_BindLua.cpp
index a057d4e60..e43f63346 100644
--- a/WickedEngine/wiSprite_BindLua.cpp
+++ b/WickedEngine/wiSprite_BindLua.cpp
@@ -120,6 +120,7 @@ namespace wi::lua
if (tex != nullptr)
{
sprite.maskResource = tex->resource;
+ sprite.params.setMaskMap(&tex->resource.GetTexture());
}
}
else
diff --git a/WickedEngine/wiTextureHelper.cpp b/WickedEngine/wiTextureHelper.cpp
index 545c52600..b8022e0af 100644
--- a/WickedEngine/wiTextureHelper.cpp
+++ b/WickedEngine/wiTextureHelper.cpp
@@ -335,7 +335,7 @@ namespace wi::texturehelper
{
for (uint32_t x = 0; x < width; ++x)
{
- const XMFLOAT2 uv = XMFLOAT2(float(x) / float(width - 1), float(y) / float(height - 1));
+ const XMFLOAT2 uv = XMFLOAT2((float(x) + 0.5f) / float(width), (float(y) + 0.5f) / float(height));
const XMVECTOR point_on_line = wi::math::ClosestPointOnLineSegment(a, b, XMLoadFloat2(&uv));
const float uv_distance = XMVectorGetX(XMVector3Length(point_on_line - a));
float gradient = wi::math::saturate(wi::math::InverseLerp(0, distance, uv_distance));
@@ -374,7 +374,7 @@ namespace wi::texturehelper
{
for (uint32_t x = 0; x < width; ++x)
{
- const XMFLOAT2 uv = XMFLOAT2(float(x) / float(width - 1), float(y) / float(height - 1));
+ const XMFLOAT2 uv = XMFLOAT2((float(x) + 0.5f) / float(width), (float(y) + 0.5f) / float(height));
const float uv_distance = wi::math::Clamp(XMVectorGetX(XMVector3Length(XMLoadFloat2(&uv) - a)), 0, distance);
float gradient = wi::math::saturate(wi::math::InverseLerp(0, distance, uv_distance));
if (has_flag(flags, GradientFlags::Inverse))
@@ -411,7 +411,7 @@ namespace wi::texturehelper
{
for (uint32_t x = 0; x < width; ++x)
{
- const XMFLOAT2 uv = XMFLOAT2(float(x) / float(width - 1), float(y) / float(height - 1));
+ const XMFLOAT2 uv = XMFLOAT2((float(x) + 0.5f) / float(width), (float(y) + 0.5f) / float(height));
const XMFLOAT2 coord = XMFLOAT2(uv.x - uv_start.x, uv.y - uv_start.y);
float gradient = wi::math::GetAngle(direction, coord) / XM_2PI;
if (has_flag(flags, GradientFlags::Inverse))
@@ -467,7 +467,7 @@ namespace wi::texturehelper
{
for (uint32_t x = 0; x < width; ++x)
{
- const XMFLOAT2 coord = XMFLOAT2(float(x) / float(width - 1) * 2 - 1, -(float(y) / float(height - 1) * 2 - 1));
+ const XMFLOAT2 coord = XMFLOAT2((float(x) + 0.5f) / float(width) * 2 - 1, -((float(y) + 0.5f) / float(height) * 2 - 1));
float gradient = wi::math::GetAngle(direction, coord) / XM_2PI;
if (counter_clockwise)
{
diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp
index c61014dd4..dc8f80814 100644
--- a/WickedEngine/wiVersion.cpp
+++ b/WickedEngine/wiVersion.cpp
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 71;
// minor bug fixes, alterations, refactors, updates
- const int revision = 397;
+ const int revision = 398;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);