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