diff --git a/bin/data/entities/gui/mainmenu/quit.json b/bin/data/entities/gui/mainmenu/quit.json index 6e269a7d..f2357641 100644 --- a/bin/data/entities/gui/mainmenu/quit.json +++ b/bin/data/entities/gui/mainmenu/quit.json @@ -12,13 +12,15 @@ }, "metadata": { "clickable": true, + "hoverable": true, "events": { "click": { "name": "system:Quit", "payload": { "scene": "StartMenu" - } + }, + "delay": 0.125 } }, diff --git a/bin/data/entities/gui/mainmenu/scripts/menu.lua b/bin/data/entities/gui/mainmenu/scripts/menu.lua index c31540b8..440e1764 100644 --- a/bin/data/entities/gui/mainmenu/scripts/menu.lua +++ b/bin/data/entities/gui/mainmenu/scripts/menu.lua @@ -15,6 +15,8 @@ local camera = controller:getComponent("Camera") local metadata = ent:getComponent("Metadata") local masterdata = scene:getComponent("Metadata") +local soundEmitter = ent:loadChild("./sound.json",true) + local children = { mainText = ent:loadChild("./main-text.json",true), circleOut = ent:loadChild("./circle-out.json",true), @@ -28,6 +30,7 @@ if not timer:running() then timer:start() end local playSound = function( key ) local url = "/ui/" .. key .. ".ogg" + soundEmitter:callHook("sound:Emit.%UID%", { filename = url }) -- local assetLoader = scene:getComponent("Asset") -- assetLoader:cache(ent:formatHookName("asset:Load.%UID%"), string.resolveURI(url)) end @@ -66,105 +69,172 @@ ent:addHook("asset:Load.%UID%", function( json ) return true end ) -if os.arch() == "Dreamcast" then - ent:bind( "tick", function(self) - -- circle in - if children.circleIn:uid() > 0 then - local transform = children.circleIn:getComponent("Transform") - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), time.current() * -0.0125 ) - end - -- circle out - if children.circleIn:uid() > 0 then - local transform = children.circleIn:getComponent("Transform") - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), time.current() * 0.0125 ) - end - if (window.keyPressed("Enter") or inputs.key("START")) and timer:elapsed() >= 1 then - timer:reset() - children.start:callHook("gui:Clicked.%UID%", {}) - end - end ) -else - ent:bind( "tick", function(self) - --if (window.keyPressed("Enter") or inputs.key("START")) and timer:elapsed() >= 1 then - if inputs.key("START") and timer:elapsed() >= 1 then - timer:reset() - children.start:callHook("gui:Clicked.%UID%", {}) - end +local selectableElements = { + children.start, + children.quit, +} +local selectableElementColors = {} +local selectedElement = 0 +local selectionColor = { 1, 0, 1, 1 } +local INPUT_DELAY = 0.2 - local static = Static.get(self) - if not static.alpha then - static.alpha = 0 +local function array_index_of( haystack, needle ) + for i, v in ipairs(haystack) do + if v == needle then + return i end + end + return 0 +end +local function onHover( payload ) + playSound( "buttonrollover" ) + selectedElement = 0 +end +local function onClick( payload ) + playSound( "buttonclickrelease" ) +end - metadata["initialized"] = true; - if static.alpha >= 1.0 then - static.alpha = 1.0 +for i, v in ipairs( selectableElements ) do + selectableElementColors[i] = nil + v:addHook("gui:HoverStart.%UID%", onHover ) + v:addHook("gui:ClickStart.%UID%", onClick ) +end + +local function handleSelectionIndex() + if (inputs.key("L_DPAD_UP") or inputs.key("Up")) and timer:elapsed() >= INPUT_DELAY then + timer:reset() + -- nothing is selected + if selectedElement == 0 then + -- set to bottom + selectedElement = #selectableElements else - static.alpha = static.alpha + time.delta() * 1.5 + selectedElement = selectedElement - 1 + -- wrap to bottom + if selectedElement <= 0 then + selectedElement = #selectableElements + end + end + playSound( "buttonrollover" ) + end + if (inputs.key("L_DPAD_DOWN") or inputs.key("Down")) and timer:elapsed() >= INPUT_DELAY then + timer:reset() + -- nothing is selected + if selectedElement == 0 then + -- set to top + selectedElement = 1 + else + selectedElement = selectedElement + 1 + -- wrap to top + if selectedElement > #selectableElements then + selectedElement = 1 + end + end + playSound( "buttonrollover" ) + end +end + +ent:bind( "tick", function(self) + handleSelectionIndex() + + local static = Static.get(self) + if not static.alpha then + static.alpha = 0 + end + + metadata["initialized"] = true; + + if static.alpha >= 1.0 then + static.alpha = 1.0 + else + static.alpha = static.alpha + time.delta() * 1.5 + end + + -- make background glow + local glow = 1 + math.sin(1.25 * time.current()) * 0.125 + metadata["color"] = { glow, glow, glow, static.alpha } + self:setComponent("Metadata", metadata) + + camera:update(true); + + -- iterate children in batch + for k, v in pairs(children) do + if v:uid() <= 0 then goto continue end + + -- set alpha + local metadata = v:getComponent("Metadata") + local index = array_index_of( selectableElements, v ) + -- mark element as hovered if selected + if 0 < selectedElement and selectedElement <= #selectableElements then + if 0 < index and index <= #selectableElements then + metadata["hovered"] = index == selectedElement + end end - -- make background glow - local glow = 1 + math.sin(1.25 * time.current()) * 0.125 - metadata["color"][1] = glow - metadata["color"][2] = glow - metadata["color"][3] = glow - metadata["alpha"] = static.alpha; - self:setComponent("Metadata", metadata) - - camera:update(true); - - -- iterate children in batch - for k, v in pairs(children) do - if v:uid() <= 0 then goto continue end - - -- set alpha - local metadata = v:getComponent("Metadata") - metadata["alpha"] = static.alpha - v:setComponent("Metadata", metadata) - - local transform = v:getComponent("Transform") - local static = Static.get(v) - -- translation - if not static.from then goto continue end - if not static.to then static.to = Vector3f(transform.position) end - if not static.delta then static.delta = 0 end - - if static.delta >= 1 then - static.delta = 1 - else - static.delta = static.delta + time.delta() * 1.5 - transform.position = Vector3f.lerp( static.from, static.to, static.delta ) + if metadata["clickable"] then + -- backup color + if selectableElementColors[index] == nil then + selectableElementColors[index] = metadata["color"] end - ::continue:: - end - - -- circle in - child = children.circleIn - if child:uid() > 0 then - local static = Static.get( child ) - - local transform = child:getComponent("Transform") - local metadata = child:getComponent("Metadata") - - -- rotation - local speed = metadata["hovered"] and 0.25 or 0.0125 - static.time = (static.time or 0) + time.delta() * -speed - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) + -- color for selection + if metadata["hovered"] then + metadata["color"] = selectionColor + -- simulate click on input press + if (inputs.key("A") or inputs.key("START") or inputs.key("Enter")) and timer:elapsed() >= INPUT_DELAY then + timer:reset() + v:callHook("gui:Clicked.%UID%", {}) + end + else + metadata["color"] = selectableElementColors[index] + end end - -- circle out - child = children.circleOut - if child:uid() > 0 then - local static = Static.get( child ) - local transform = child:getComponent("Transform") - local metadata = child:getComponent("Metadata") + metadata["color"][4] = static.alpha + v:setComponent("Metadata", metadata) - -- rotation - local speed = metadata["hovered"] and 0.25 or 0.0125 - static.time = (static.time or 0) + time.delta() * speed - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) + local transform = v:getComponent("Transform") + local static = Static.get(v) + -- translation + if not static.from then goto continue end + if not static.to then static.to = Vector3f(transform.position) end + if not static.delta then static.delta = 0 end + + if static.delta >= 1 then + static.delta = 1 + else + static.delta = static.delta + time.delta() * 1.5 + transform.position = Vector3f.lerp( static.from, static.to, static.delta ) end - end ) -end \ No newline at end of file + + + ::continue:: + end + + -- circle in + child = children.circleIn + if child:uid() > 0 then + local static = Static.get( child ) + + local transform = child:getComponent("Transform") + local metadata = child:getComponent("Metadata") + + -- rotation + local speed = metadata["hovered"] and 0.25 or 0.0125 + static.time = (static.time or 0) + time.delta() * -speed + transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) + end + -- circle out + child = children.circleOut + if child:uid() > 0 then + local static = Static.get( child ) + + local transform = child:getComponent("Transform") + local metadata = child:getComponent("Metadata") + + -- rotation + local speed = metadata["hovered"] and 0.25 or 0.0125 + static.time = (static.time or 0) + time.delta() * speed + transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) + end +end ) \ No newline at end of file diff --git a/bin/data/entities/gui/mainmenu/sound.json b/bin/data/entities/gui/mainmenu/sound.json index e69de29b..521052cd 100644 --- a/bin/data/entities/gui/mainmenu/sound.json +++ b/bin/data/entities/gui/mainmenu/sound.json @@ -0,0 +1,38 @@ +{ + "type": "Object", + "name": "Sound Emitter", + "ignore": false, + "assets": [ + ], + "behaviors": [ + "SoundEmitterBehavior" + ], + "transform": { + "position": [ 0, 0, 0 ], + "rotation": { + "axis": [ 0, 1, 0 ], + "angle": 0 + }, + "scale": [ 1, 1, 1 ] + }, + "system": { + "hot reload": { + "enabled": true + }, + "defaults": { + "render": true, + "asset load": true + }, + "load": { + "ignore": true + } + }, + "metadata": { + "audio": { + "spatial": false, + "loop": false, + "volume": "sfx", + "rolloffFactor": 2 + } + } +} \ No newline at end of file diff --git a/bin/data/entities/gui/mainmenu/start.json b/bin/data/entities/gui/mainmenu/start.json index 6c39fe10..aa1a9c1e 100644 --- a/bin/data/entities/gui/mainmenu/start.json +++ b/bin/data/entities/gui/mainmenu/start.json @@ -12,10 +12,12 @@ }, "metadata": { "clickable": true, + "hoverable": true, "events": { "click": { "name": "game:Scene.Load", - "payload": { "scene": "SourceEngine" } + "payload": { "scene": "SourceEngine" }, + "delay": 0.125 } }, diff --git a/bin/data/entities/gui/pause/close.json b/bin/data/entities/gui/pause/close.json index 64ac04f9..5fbe34da 100644 --- a/bin/data/entities/gui/pause/close.json +++ b/bin/data/entities/gui/pause/close.json @@ -12,10 +12,12 @@ }, "metadata": { "clickable": true, + "hoverable": true, "events": { "click": { "name": "menu:Close.%P-UID%", - "payload": {} + "payload": {}, + "delay": 0.125 } }, diff --git a/bin/data/entities/gui/pause/quit.json b/bin/data/entities/gui/pause/quit.json index 92021d7d..f81857ad 100644 --- a/bin/data/entities/gui/pause/quit.json +++ b/bin/data/entities/gui/pause/quit.json @@ -12,6 +12,7 @@ }, "metadata": { "clickable": true, + "hoverable": true, "events": { "click": [ { @@ -25,7 +26,8 @@ "scope": "scene", "delay": 0 } - } + }, + "delay": 0.125 } ] }, diff --git a/bin/data/entities/gui/pause/scripts/menu.lua b/bin/data/entities/gui/pause/scripts/menu.lua index 4c9869ce..65d801dd 100644 --- a/bin/data/entities/gui/pause/scripts/menu.lua +++ b/bin/data/entities/gui/pause/scripts/menu.lua @@ -46,207 +46,218 @@ destination(children.quit, -1.5, nil, 0) local playSound = function( key ) local url = "/ui/" .. key .. ".ogg" + soundEmitter:callHook("sound:Emit.%UID%", { filename = url }) -- local assetLoader = scene:getComponent("Asset") -- assetLoader:cache(soundEmitter:formatHookName("asset:Load.%UID%"), string.resolveURI(url), "") end ent:addHook("menu:Close.%UID%", function( json ) - playSound("menu close") + --playSound("menu close") if metadata["system"]["hooks"] == nil then metadata["system"]["hooks"] = {} end metadata["system"]["hooks"]["onClose"] = json["callback"]; metadata["system"]["closing"] = true; ent:setComponent("Metadata", metadata) end ) -playSound("menu open") +--playSound("menu open") -if os.arch() == "Dreamcast" then - ent:bind( "tick", function(self) - local static = Static.get(self) - if not static.alpha then +local selectableElements = { + children.closeOption, + children.quit, +} +local selectableElementColors = {} +local selectedElement = 0 +local selectionColor = { 1, 0, 1, 1 } +local INPUT_DELAY = 0.2 + +local function array_index_of( haystack, needle ) + for i, v in ipairs(haystack) do + if v == needle then + return i + end + end + return 0 +end + +local function onHover( payload ) + playSound( "buttonrollover" ) + selectedElement = 0 +end +local function onClick( payload ) + playSound( "buttonclickrelease" ) +end + +for i, v in ipairs( selectableElements ) do + selectableElementColors[i] = nil + v:addHook("gui:HoverStart.%UID%", onHover ) + v:addHook("gui:ClickStart.%UID%", onClick ) +end + +local function handleSelectionIndex() + if (inputs.key("L_DPAD_UP") or inputs.key("Up")) and timer:elapsed() >= INPUT_DELAY then + timer:reset() + -- nothing is selected + if selectedElement == 0 then + -- set to bottom + selectedElement = #selectableElements + else + selectedElement = selectedElement - 1 + -- wrap to bottom + if selectedElement <= 0 then + selectedElement = #selectableElements + end + end + playSound( "buttonrollover" ) + end + if (inputs.key("L_DPAD_DOWN") or inputs.key("Down")) and timer:elapsed() >= INPUT_DELAY then + timer:reset() + -- nothing is selected + if selectedElement == 0 then + -- set to top + selectedElement = 1 + else + selectedElement = selectedElement + 1 + -- wrap to top + if selectedElement > #selectableElements then + selectedElement = 1 + end + end + playSound( "buttonrollover" ) + end +end + +ent:bind( "tick", function(self) + handleSelectionIndex() + + local static = Static.get(self) + if not static.alpha then + static.alpha = 0 + end + + if (window.keyPressed("Escape") or inputs.key("START")) and timer:elapsed() >= INPUT_DELAY then + timer:reset() + self:callHook("menu:Close.%UID%", {}) + end + + -- handle closing + if metadata["system"]["closing"] then + if static.alpha <= 0 then + static.alpha = 0 + metadata["system"]["closing"] = false + metadata["system"]["closed"] = true + else + static.alpha = static.alpha - time.delta() + end + elseif metadata["system"]["closed"] then + timer:stop() + local callback = metadata["system"]["hooks"]["onClose"] + if callback then + local payload = callback["payload"] + local target = self + if callback["scope"] == "parent" then + target = ent:getParent() + elseif callback["scope"] == "scene" then + target = scene + end + + if type(callback["delay"]) == "number" and target:uid() ~= self:uid() then + target:queueHook( callback["name"], payload, callback["delay"] ); + else + target:callHook( callback["name"], payload ); + end + end + + entities.destroy(self) + -- scene:queueHook("system:Destroy", { uid = self:uid() }, 0) + return + else + if not metadata["initialized"] then static.alpha = 0 end - - if (window.keyPressed("Escape") or inputs.key("START")) and timer:elapsed() >= 1 then - timer:reset() - self:callHook("menu:Close.%UID%", {}) - end - - -- handle closing - if metadata["system"]["closing"] then - if static.alpha <= 0 then - static.alpha = 0 - metadata["system"]["closing"] = false - metadata["system"]["closed"] = true - else - static.alpha = static.alpha - time.delta() - end - elseif metadata["system"]["closed"] then - timer:stop() - local callback = metadata["system"]["hooks"]["onClose"] - if callback then - local payload = callback["payload"] - local target = self - if callback["scope"] == "parent" then - target = ent:getParent() - elseif callback["scope"] == "scene" then - target = scene - end - - if type(callback["delay"]) == "number" and target:uid() ~= self:uid() then - target:queueHook( callback["name"], payload, callback["delay"] ); - else - target:callHook( callback["name"], payload ); - end - end - - controller:callHook("system:Control.%UID%", { - control = true - }) - - entities.destroy(self) - -- scene:queueHook("system:Destroy", { uid = self:uid() }, 0) - return + metadata["initialized"] = true; + if static.alpha >= 1.0 then + static.alpha = 1.0 else - if not metadata["initialized"] then - static.alpha = 0 - end - metadata["initialized"] = true; - if static.alpha >= 1.0 then - static.alpha = 1.0 - else - static.alpha = static.alpha + time.delta() * 1.5 + static.alpha = static.alpha + time.delta() * 1.5 + end + end + + metadata["color"][4] = static.alpha; + + -- set alphas + for k, v in pairs(children) do + if v:uid() <= 0 then goto continue end + + -- set alpha + local metadata = v:getComponent("Metadata") + local index = array_index_of( selectableElements, v ) + -- mark element as hovered if selected + if 0 < selectedElement and selectedElement <= #selectableElements then + if 0 < index and index <= #selectableElements then + metadata["hovered"] = index == selectedElement end end - -- circle in - if children.circleIn:uid() > 0 then - local static = Static.get( children.circleIn ) - local transform = children.circleIn:getComponent("Transform") - - -- rotation - local speed = 0.0125 - static.time = (static.time or 0) + time.delta() * -speed - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) - end - -- circle out - if children.circleOut:uid() > 0 then - local static = Static.get( children.circleOut ) - - local transform = children.circleOut:getComponent("Transform") - - -- rotation - local speed = 0.0125 - static.time = (static.time or 0) + time.delta() * speed - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) - end - end ) -else - ent:bind( "tick", function(self) - local static = Static.get(self) - if not static.alpha then - static.alpha = 0 - end - - if (window.keyPressed("Escape") or inputs.key("START")) and timer:elapsed() >= 1 then - timer:reset() - self:callHook("menu:Close.%UID%", {}) - end - - -- handle closing - if metadata["system"]["closing"] then - if static.alpha <= 0 then - static.alpha = 0 - metadata["system"]["closing"] = false - metadata["system"]["closed"] = true - else - static.alpha = static.alpha - time.delta() + if metadata["clickable"] then + -- backup color + if selectableElementColors[index] == nil then + selectableElementColors[index] = metadata["color"] end - elseif metadata["system"]["closed"] then - timer:stop() - local callback = metadata["system"]["hooks"]["onClose"] - if callback then - local payload = callback["payload"] - local target = self - if callback["scope"] == "parent" then - target = ent:getParent() - elseif callback["scope"] == "scene" then - target = scene - end - - if type(callback["delay"]) == "number" and target:uid() ~= self:uid() then - target:queueHook( callback["name"], payload, callback["delay"] ); - else - target:callHook( callback["name"], payload ); + + -- color for selection + if metadata["hovered"] then + metadata["color"] = selectionColor + -- simulate click on input press + if (inputs.key("A") or inputs.key("Enter")) and timer:elapsed() >= INPUT_DELAY then + timer:reset() + v:callHook("gui:Clicked.%UID%", {}) end + else + metadata["color"] = selectableElementColors[index] end + end + metadata["color"][4] = static.alpha + v:setComponent("Metadata", metadata) - entities.destroy(self) - -- scene:queueHook("system:Destroy", { uid = self:uid() }, 0) - return + local transform = v:getComponent("Transform") + local static = Static.get(v) + -- translation + if not static.from then goto continue end + if not static.to then static.to = Vector3f(transform.position) end + if not static.delta then static.delta = 0 end + + if static.delta >= 1 then + static.delta = 1 else - if not metadata["initialized"] then - static.alpha = 0 - end - metadata["initialized"] = true; - if static.alpha >= 1.0 then - static.alpha = 1.0 - else - static.alpha = static.alpha + time.delta() * 1.5 - end + static.delta = static.delta + time.delta() * 1.5 + transform.position = Vector3f.lerp( static.from, static.to, static.delta ) end + ::continue:: + end - metadata["alpha"] = static.alpha; - -- set alphas - for k, v in pairs(children) do - if v:uid() <= 0 then goto continue end + -- circle in + if children.circleIn:uid() > 0 then + local static = Static.get( children.circleIn ) - -- set alpha - local metadata = v:getComponent("Metadata") - metadata["alpha"] = static.alpha + local transform = children.circleIn:getComponent("Transform") + local metadata = children.circleIn:getComponent("Metadata") - local transform = v:getComponent("Transform") - local static = Static.get(v) - -- translation - if not static.from then goto continue end - if not static.to then static.to = Vector3f(transform.position) end - if not static.delta then static.delta = 0 end + -- rotation + local speed = metadata["hovered"] and 0.25 or 0.0125 + static.time = (static.time or 0) + time.delta() * -speed + transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) + end + -- circle out + if children.circleOut:uid() > 0 then + local static = Static.get( children.circleOut ) - if static.delta >= 1 then - static.delta = 1 - else - static.delta = static.delta + time.delta() * 1.5 - transform.position = Vector3f.lerp( static.from, static.to, static.delta ) - v:setComponent("Metadata", metadata) - end + local transform = children.circleOut:getComponent("Transform") + local metadata = children.circleOut:getComponent("Metadata") - ::continue:: - end - - -- circle in - if children.circleIn:uid() > 0 then - local static = Static.get( children.circleIn ) - - local transform = children.circleIn:getComponent("Transform") - local metadata = children.circleIn:getComponent("Metadata") - - -- rotation - local speed = metadata["hovered"] and 0.25 or 0.0125 - static.time = (static.time or 0) + time.delta() * -speed - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) - end - -- circle out - if children.circleOut:uid() > 0 then - local static = Static.get( children.circleOut ) - - local transform = children.circleOut:getComponent("Transform") - local metadata = children.circleOut:getComponent("Metadata") - - -- rotation - local speed = metadata["hovered"] and 0.25 or 0.0125 - static.time = (static.time or 0) + time.delta() * speed - transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) - end - end ) -end \ No newline at end of file + -- rotation + local speed = metadata["hovered"] and 0.25 or 0.0125 + static.time = (static.time or 0) + time.delta() * speed + transform.orientation = Quaternion.axisAngle( Vector3f(0, 0, 1), static.time ) + end +end ) \ No newline at end of file diff --git a/bin/data/entities/model.json b/bin/data/entities/model.json index 1fa9da21..9d820db7 100644 --- a/bin/data/entities/model.json +++ b/bin/data/entities/model.json @@ -76,7 +76,7 @@ "shadows": true }, "stream": { - "tag": "worldspawn", + "tag": "", // worldspawn", "player": "info_player_spawn", "enabled": true, // "auto", "radius": 16, diff --git a/bin/data/entities/player.json b/bin/data/entities/player.json index 0841a05c..853973bf 100644 --- a/bin/data/entities/player.json +++ b/bin/data/entities/player.json @@ -20,7 +20,7 @@ { "filename": "./playerModel.json", "delay": 1 }, // "./playerModel.json", "./playerLight.json", - "./playerHands.json", + // "./playerHands.json", "./scripts/player.lua" ], "system": { diff --git a/bin/data/scenes/sourceengine/animal_crossing.json b/bin/data/scenes/sourceengine/animal_crossing.json index 5e4d3b1b..4200ffca 100644 --- a/bin/data/scenes/sourceengine/animal_crossing.json +++ b/bin/data/scenes/sourceengine/animal_crossing.json @@ -2,12 +2,12 @@ "import": "./base_sourceengine.json", "assets": [ // { "filename": "./models/animal_crossing.glb" }, - { "filename": "./models/animal_crossing/graph.json" }, + { "filename": "./models/animal_crossing/graph.json" }/*, // { "filename": "./models/animal_crossing_small.glb" }, // { "filename": "./models/animal_crossing_small/graph.json" }, { "filename": "/craeture.json", "delay": 2.0 }, - { "filename": "/craeture2.json", "delay": 2.0 } + { "filename": "/craeture2.json", "delay": 2.0 }*/ ], "metadata": { "graph": { diff --git a/bin/data/scenes/sourceengine/base_sourceengine.json b/bin/data/scenes/sourceengine/base_sourceengine.json index e95f131a..9c359469 100644 --- a/bin/data/scenes/sourceengine/base_sourceengine.json +++ b/bin/data/scenes/sourceengine/base_sourceengine.json @@ -11,12 +11,12 @@ // exact matches "worldspawn": { "physics": { "type": "mesh", "static": true }, - "grid": { "size": [16,1,16], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true }, + "grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true }, "optimize meshlets": { "simplify": 0.125, "print": false }, "unwrap mesh": true }, "worldspawn_skybox": { - "grid": { "size": [16,1,16], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true }, + "grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true }, "optimize meshlets": { "simplify": 0.125, "print": false }, "unwrap mesh": true }, diff --git a/bin/data/scenes/sourceengine/ss2_medsci1.json b/bin/data/scenes/sourceengine/ss2_medsci1.json index ad352bf7..c963a955 100644 --- a/bin/data/scenes/sourceengine/ss2_medsci1.json +++ b/bin/data/scenes/sourceengine/ss2_medsci1.json @@ -4,16 +4,16 @@ // { "filename": "./models/ss2_medsci1.glb" } // { "filename": "./models/ss2_medsci1/graph.json" } // { "filename": "./models/ss2_medsci1_small.glb" } - { "filename": "./models/ss2_medsci1_small/graph.json" } + // { "filename": "./models/ss2_medsci1_small/graph.json" } // { "filename": "./models/ss2_medsci1_smallish.glb" } - // { "filename": "./models/ss2_medsci1_smallish/graph.json" } + { "filename": "./models/ss2_medsci1_smallish/graph.json" } ], "metadata": { "graph": { "bgm": "./audio/music/medsci1.ogg", "tags": { - "/^prop_/": { "action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } } }, - "/^func_/": { "action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } } } + "/^prop_/": { "ignore": true, "action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } } }, + "/^func_/": { "ignore": true, "action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } } } } } } diff --git a/bin/dreamcast/data/config.json b/bin/dreamcast/data/config.json index dba9288d..44a519cc 100644 --- a/bin/dreamcast/data/config.json +++ b/bin/dreamcast/data/config.json @@ -17,7 +17,7 @@ } }, "graph": { - "initial buffer elements": 128 + "initial buffer elements": 256 }, "ext": { "opengl": { diff --git a/bin/exe/default/renderer b/bin/exe/default/renderer index 91caa7c1..53395cff 100644 --- a/bin/exe/default/renderer +++ b/bin/exe/default/renderer @@ -1 +1 @@ -opengl \ No newline at end of file +vulkan \ No newline at end of file diff --git a/dep/README.md b/dep/README.md index 8544632f..98f2aead 100644 --- a/dep/README.md +++ b/dep/README.md @@ -5,4 +5,15 @@ This folder stores any code for external dependencies because my environment has Separate per-target copies of dependencies may exist here as well, such as: * Dreamcast -All code under this folder is not mine and are licensed under their respective licenses. \ No newline at end of file +All code under this folder is not mine and are licensed under their respective licenses. + +## Forked Dependencies + +Some dependencies of mine rely on some modifications of the original dependencies for extra features or to function on other platforms. + +* [nlohmann/json](https://github.com/nlohmann/json): Dreamcast only; requires some hacks for `m4-single-only` due to `sizeof(float) == sizeof(double)` (for prior toolchains, this allegedly changed but I still get a crash with an unmodified `nlohmann/json`) + * this is a header-only project so its changes live locally under `./dep/dreamcast/include/nlohmann/` +* [DanielChappuis/reactphysics3d](https://git.ecker.tech/ecker/reactphysics3d): adds in support for `float16`/`bfloat16`/quantized `uint16_t` triangle meshes + * Dreamcast requires some edits due to `sizeof(int) != sizeof(int32)` / `sizeof(uint) != sizeof(uint32)`, extra compile flags, and disabling exceptions +* [simulant/GLdc](https://git.ecker.tech/ecker/GLdc/): Dreamcast only; adds in support for `float16`/`bfloat16`/quantized `uint16_t` vertex data +* [GPUOpen-Effects/FidelityFX-FSR2](https://git.ecker.tech/ecker/FidelityFX-FSR2): modifications required for compiling under GCC (despite being a soft-dependency that I'm not making use of anyways) \ No newline at end of file diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index ff0fa416..f789533b 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -158,8 +158,8 @@ namespace uf { void UF_API render(); void UF_API destroy( bool soft = false ); - void UF_API initialize( uf::Object& ); - void UF_API initialize( pod::Graph::Storage& ); + void UF_API initialize( uf::Object&, size_t = uf::graph::initialBufferElements ); + void UF_API initialize( pod::Graph::Storage&, size_t = uf::graph::initialBufferElements ); void UF_API tick( uf::Object& ); void UF_API tick( pod::Graph::Storage& ); void UF_API render( uf::Object& ); diff --git a/engine/src/engine/ext/gui/behavior.cpp b/engine/src/engine/ext/gui/behavior.cpp index 4967a32a..2c8f3dae 100644 --- a/engine/src/engine/ext/gui/behavior.cpp +++ b/engine/src/engine/ext/gui/behavior.cpp @@ -327,22 +327,41 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { int mouseX = payload.mouse.position.x; int mouseY = payload.mouse.position.y; } + + if ( !metadata.ui.click.ed && clicked ) { + this->callHook("gui:ClickStart.%UID%", payload); + } else if ( metadata.ui.click.ed && !clicked ) { + this->callHook("gui:ClickEnd.%UID%", payload); + } metadata.ui.click.ed = clicked; - if ( clicked ) { this->callHook("gui:Clicked.%UID%", payload); } + this->callHook("gui:Mouse.Clicked.%UID%", payload); } ); - this->addHook( "gui:Clicked.%UID%", [&]( ext::json::Value& json ){ + this->addHook( "gui:ClickStart.%UID%", [&]( pod::payloads::windowMouseClick& payload ){ + uf::Serializer jsonPayload; + this->callHook("gui:ClickStart.%UID%", jsonPayload); + }); + this->addHook( "gui:ClickEnd.%UID%", [&]( pod::payloads::windowMouseClick& payload ){ + uf::Serializer jsonPayload; + this->callHook("gui:ClickEnd.%UID%", jsonPayload); + }); + this->addHook( "gui:Clicked.%UID%", [&](ext::json::Value& json){ + if ( ext::json::isNull( json ) ) return; + pod::payloads::windowMouseClick payload; this->callHook("gui:Clicked.%UID%", payload); }); this->addHook( "gui:Clicked.%UID%", [&](pod::payloads::windowMouseClick& payload){ + uf::Serializer jsonPayload; + this->callHook("gui:Clicked.%UID%", jsonPayload); + if ( ext::json::isObject( metadataJson["events"]["click"] ) ) { ext::json::Value event = metadataJson["events"]["click"]; metadataJson["events"]["click"] = ext::json::array(); @@ -398,36 +417,58 @@ void ext::GuiBehavior::initialize( uf::Object& self ) { int mouseX = payload.mouse.position.x; int mouseY = payload.mouse.position.y; } - - metadata.ui.hover.ed = hovered; - if ( hovered && hoverTimer.elapsed().asDouble() >= 1 ) { - hoverTimer.reset(); + uf::Serializer jsonPayload = ext::json::null(); + + // to-do: do something about trying to trigger json-bound hooks + if ( !metadata.ui.hover.ed && hovered ) { + this->callHook("gui:HoverStart.%UID%", payload); + } else if ( metadata.ui.hover.ed && !hovered ) { + this->callHook("gui:HoverEnd.%UID%", payload); + } else if ( hovered ) { /*&& hoverTimer.elapsed().asDouble() >= 1 ) { + hoverTimer.reset();*/ this->callHook("gui:Hovered.%UID%", payload); } + + metadata.ui.hover.ed = hovered; this->callHook("gui:Mouse.Moved.%UID%", payload); }); + this->addHook( "gui:HoverStart.%UID%", [&]( pod::payloads::windowMouseMoved& payload ){ + uf::Serializer jsonPayload; + this->callHook("gui:HoverStart.%UID%", jsonPayload); + }); + this->addHook( "gui:HoverEnd.%UID%", [&]( pod::payloads::windowMouseMoved& payload ){ + uf::Serializer jsonPayload; + this->callHook("gui:HoverEnd.%UID%", jsonPayload); + }); this->addHook( "gui:Hovered.%UID%", [&](ext::json::Value& json){ + if ( ext::json::isNull( json ) ) return; + + pod::payloads::windowMouseMoved payload; + this->callHook("gui:Hovered.%UID%", payload); + }); + this->addHook( "gui:Hovered.%UID%", [&](pod::payloads::windowMouseMoved& payload){ + uf::Serializer jsonPayload; + this->callHook("gui:Hovered.%UID%", jsonPayload); + if ( ext::json::isObject( metadataJson["events"]["hover"] ) ) { ext::json::Value event = metadataJson["events"]["hover"]; - metadataJson["events"]["hover"] = ext::json::array(); //Json::arrayValue; + metadataJson["events"]["hover"] = ext::json::array(); metadataJson["events"]["hover"][0] = event; } else if ( !ext::json::isArray( metadataJson["events"]["hover"] ) ) { - this->getParent().as().callHook("gui:Clicked.%UID%", json); + this->getParent().as().callHook("gui:Hovered.%UID%", payload); return; } for ( int i = 0; i < metadataJson["events"]["hover"].size(); ++i ) { ext::json::Value event = metadataJson["events"]["hover"][i]; ext::json::Value payload = event["payload"]; - float delay = event["delay"].as(); - if ( event["delay"].is() ) { + if ( event["delay"].is() ) { this->queueHook(event["name"].as(), payload, event["delay"].as()); } else { this->callHook(event["name"].as(), payload ); } } - return; }); } } diff --git a/engine/src/engine/ext/player/behavior.cpp b/engine/src/engine/ext/player/behavior.cpp index 31140cb4..de02b187 100644 --- a/engine/src/engine/ext/player/behavior.cpp +++ b/engine/src/engine/ext/player/behavior.cpp @@ -513,7 +513,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { uf::transform::rotate( cameraTransform, cameraTransform.right, lookDelta.y ); } else metadata.camera.limit.current.y -= lookDelta.y; } - } else { + } else if ( metadata.system.control ) { if ( keys.lookRight ^ keys.lookLeft ) { if ( collider.body ) uf::physics::impl::applyRotation( collider, transform.up, speed.rotate * (keys.lookRight ? 1 : -1) ); else uf::transform::rotate( transform, transform.up, speed.rotate * (keys.lookRight ? 1 : -1) ); diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 479cc0da..f76264b0 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -132,11 +132,8 @@ namespace { } } -#if UF_ENV_DREAMCAST - size_t uf::graph::initialBufferElements = 256; -#else - size_t uf::graph::initialBufferElements = 1024; -#endif +size_t uf::graph::initialBufferElements = 1024; + bool uf::graph::globalStorage; pod::Graph::Storage uf::graph::storage; @@ -1834,18 +1831,19 @@ void uf::graph::initialize() { if ( uf::graph::globalStorage ) return uf::graph::initialize( uf::graph::storage ); return uf::graph::initialize( uf::scene::getCurrentScene() ); } -void uf::graph::initialize( uf::Object& object ) { - return uf::graph::initialize( object.getComponent() ); +void uf::graph::initialize( uf::Object& object, size_t initialElements ) { + return uf::graph::initialize( object.getComponent(), initialElements ); } -void uf::graph::initialize( pod::Graph::Storage& storage ) { +void uf::graph::initialize( pod::Graph::Storage& storage, size_t initialElements ) { storage.buffers.camera.initialize( (const void*) nullptr, sizeof(pod::Camera::Viewports), uf::renderer::enums::Buffer::UNIFORM ); - storage.buffers.drawCommands.initialize( (const void*) nullptr, sizeof(pod::DrawCommand) * uf::graph::initialBufferElements, uf::renderer::enums::Buffer::STORAGE ); - storage.buffers.instance.initialize( (const void*) nullptr, sizeof(pod::Instance) * uf::graph::initialBufferElements, uf::renderer::enums::Buffer::STORAGE ); - storage.buffers.instanceAddresses.initialize( (const void*) nullptr, sizeof(pod::Instance::Addresses) * uf::graph::initialBufferElements, uf::renderer::enums::Buffer::STORAGE ); - storage.buffers.joint.initialize( (const void*) nullptr, sizeof(pod::Matrix4f) * uf::graph::initialBufferElements, uf::renderer::enums::Buffer::STORAGE ); - storage.buffers.material.initialize( (const void*) nullptr, sizeof(pod::Material) * uf::graph::initialBufferElements, uf::renderer::enums::Buffer::STORAGE ); - storage.buffers.texture.initialize( (const void*) nullptr, sizeof(pod::Texture) * uf::graph::initialBufferElements, uf::renderer::enums::Buffer::STORAGE ); - storage.buffers.light.initialize( (const void*) nullptr, sizeof(pod::Light) * uf::graph::initialBufferElements, uf::renderer::enums::Buffer::STORAGE ); + // to-do: check if opengl really needs these + storage.buffers.drawCommands.initialize( (const void*) nullptr, sizeof(pod::DrawCommand) * initialElements, uf::renderer::enums::Buffer::STORAGE ); + storage.buffers.instance.initialize( (const void*) nullptr, sizeof(pod::Instance) * initialElements, uf::renderer::enums::Buffer::STORAGE ); + storage.buffers.instanceAddresses.initialize( (const void*) nullptr, sizeof(pod::Instance::Addresses) * initialElements, uf::renderer::enums::Buffer::STORAGE ); + storage.buffers.joint.initialize( (const void*) nullptr, sizeof(pod::Matrix4f) * initialElements, uf::renderer::enums::Buffer::STORAGE ); + storage.buffers.material.initialize( (const void*) nullptr, sizeof(pod::Material) * initialElements, uf::renderer::enums::Buffer::STORAGE ); + storage.buffers.texture.initialize( (const void*) nullptr, sizeof(pod::Texture) * initialElements, uf::renderer::enums::Buffer::STORAGE ); + storage.buffers.light.initialize( (const void*) nullptr, sizeof(pod::Light) * initialElements, uf::renderer::enums::Buffer::STORAGE ); } void uf::graph::tick() { if ( uf::graph::globalStorage ) return uf::graph::tick( uf::graph::storage ); @@ -2037,6 +2035,7 @@ void uf::graph::destroy( pod::Graph::Storage& storage, bool soft ) { } void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { if ( !(0 <= node.mesh && node.mesh < graph.meshes.size()) ) return; + if ( !node.entity ) return; auto& scene = uf::scene::getCurrentScene(); auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); @@ -2074,7 +2073,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { // disable if not tagged for streaming // to-do: check tag - if ( node.name != graph.settings.stream.tag ) { + if ( graph.settings.stream.tag != "" && node.name != graph.settings.stream.tag ) { radius = 0; } diff --git a/engine/src/ext/json/json.cpp b/engine/src/ext/json/json.cpp index aa455f0f..f7bd3373 100644 --- a/engine/src/ext/json/json.cpp +++ b/engine/src/ext/json/json.cpp @@ -42,7 +42,7 @@ ext::json::Value ext::json::find( const uf::stl::string& needle, const ext::json if ( needle == key ) { exact = value; breaks = true; - } else if ( uf::string::isRegex( key ) && uf::string::matched( needle, key ) ) { + } else if ( uf::string::isRegex( key ) && uf::string::matched( needle, key ) && ext::json::isNull( regexed ) ) { regexed = value; } }); diff --git a/engine/src/ext/lua/usertypes/object.cpp b/engine/src/ext/lua/usertypes/object.cpp index 28ff99ea..d43e0d90 100644 --- a/engine/src/ext/lua/usertypes/object.cpp +++ b/engine/src/ext/lua/usertypes/object.cpp @@ -196,13 +196,24 @@ namespace binds { uf::Object& getParent( uf::Object& self ){ return self.getParent().as(); } - void addHook( uf::Object& self, const uf::stl::string& name, sol::function function ) { - self.addHook( name, [function](ext::json::Value& json){ + void addHook( uf::Object& self, const uf::stl::string& name, sol::protected_function fun ) { + self.addHook( name, [fun](ext::json::Value& json){ + // cringe + if ( ext::json::isNull( json ) ) { + auto result = fun(); + if ( !result.valid() ) { + sol::error err = result; + uf::iostream << err.what() << "\n"; + return; + } + return; + } + uf::stl::string payload = json.dump(); auto decoded = ext::lua::decode( payload ); if ( !decoded ) return; sol::table table = decoded.value(); - auto result = function( table ); + auto result = fun( table ); if ( !result.valid() ) { sol::error err = result; uf::iostream << err.what() << "\n"; diff --git a/engine/src/ext/opengl/opengl.cpp b/engine/src/ext/opengl/opengl.cpp index 6922a51a..57900c76 100644 --- a/engine/src/ext/opengl/opengl.cpp +++ b/engine/src/ext/opengl/opengl.cpp @@ -217,7 +217,7 @@ void ext::opengl::initialize() { renderMode->initialize(device); } - uf::graph::initialize(); + // uf::graph::initialize(); auto tasks = uf::thread::schedule(settings::invariant::multithreadedRecording); for ( auto& renderMode : renderModes ) { if ( !renderMode ) continue; diff --git a/engine/src/spec/window/dreamcast.cpp b/engine/src/spec/window/dreamcast.cpp index a4293a03..f25dbe39 100644 --- a/engine/src/spec/window/dreamcast.cpp +++ b/engine/src/spec/window/dreamcast.cpp @@ -323,6 +323,8 @@ uf::stl::string spec::dreamcast::pvr_malloc_stats( bool verbose ) { spec::dreamcast::Window::Window() : m_context(NULL) {} void spec::dreamcast::Window::create( const spec::dreamcast::Window::vector_t& _size, const spec::dreamcast::Window::title_t& title ) { + // gdb_init(); // i guess this is specifically when using dcload and not an emulator with gdb support...... + ::keyboard.device = maple_enum_type(1, MAPLE_FUNC_KEYBOARD); this->setSize(_size); diff --git a/makefiles/dreamcast.gcc.make b/makefiles/dreamcast.gcc.make index 99ba2d3d..9b7370aa 100644 --- a/makefiles/dreamcast.gcc.make +++ b/makefiles/dreamcast.gcc.make @@ -4,7 +4,7 @@ CC = gcc CXX = $(KOS_CCPLUS) RENDERER = opengl TARGET_EXTENSION = elf -OPTIMIZATIONS = -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -fstrict-aliasing -ffast-math -fno-unroll-all-loops -fno-optimize-sibling-calls -fschedule-insns2 -fomit-frame-pointer -DUF_NO_EXCEPTIONS -fno-exceptions -flto -g +OPTIMIZATIONS = -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -fstrict-aliasing -ffast-math -fno-unroll-all-loops -fno-optimize-sibling-calls -fschedule-insns2 -fomit-frame-pointer -DUF_NO_EXCEPTIONS -fno-exceptions -g # -flto WARNINGS = -Wno-attributes -Wno-conversion-null FLAGS += $(KOS_CPPFLAGS) -m4-single -std=c++17 $(OPTIMIZATIONS) $(WARNINGS) -fdiagnostics-color=always INCS += $(KOS_INC_PATHS) -I/opt/dreamcast/sh-elf/sh-elf/include