odd bug fixes involving entities that load more than one lua script (nasty hack), issue with audio not streaming from non-disk VFSs, more valve compliance (doors, IO connectivity, ambience_generic)
This commit is contained in:
parent
22fb850709
commit
e4ad164203
@ -4,7 +4,7 @@
|
||||
"start": "StartMenu",
|
||||
"matrix": { "reverseInfinite": true },
|
||||
"lights": { "enabled": true,
|
||||
"lightmaps": false,
|
||||
"lightmaps": true,
|
||||
"max": 32,
|
||||
"shadows": {
|
||||
"enabled": true,
|
||||
|
||||
6
bin/data/entities/ambient_generic.json
Normal file
6
bin/data/entities/ambient_generic.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"assets": ["./scripts/ambient_generic.lua"],
|
||||
"behaviors": [
|
||||
"SoundEmitterBehavior"
|
||||
]
|
||||
}
|
||||
@ -7,8 +7,8 @@
|
||||
"physics": {
|
||||
"mass": 0,
|
||||
// "inertia": [0, 0, 0],
|
||||
// "type": "bounding box"
|
||||
"type": "mesh"
|
||||
"type": "bounding box"
|
||||
// "type": "mesh"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,7 +30,7 @@ local timer = Timer.new()
|
||||
if not timer:running() then timer:start() end
|
||||
|
||||
local playSound = function( key )
|
||||
local url = "/ui/" .. key .. ".ogg"
|
||||
local url = "/ui/" .. key .. ".wav"
|
||||
soundEmitter:callHook("sound:Emit.%UID%", { filename = url })
|
||||
-- local assetLoader = scene:getComponent("Asset")
|
||||
-- assetLoader:cache(ent:formatHookName("asset:Load.%UID%"), string.resolveURI(url))
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
"events": {
|
||||
"click": {
|
||||
"name": "game:Scene.Load",
|
||||
"payload": { "scene": "VBSP" },
|
||||
"payload": { "scene": "SourceEngine" },
|
||||
"delay": 0.125
|
||||
}
|
||||
},
|
||||
|
||||
3
bin/data/entities/io.json
Normal file
3
bin/data/entities/io.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"assets": ["./scripts/io.lua"]
|
||||
}
|
||||
@ -26,7 +26,7 @@
|
||||
"bias": {
|
||||
"constant": 1.25,
|
||||
"slope": 1.75,
|
||||
"shader": 0.000005 // 0.000005 //0.000000005
|
||||
"shader": 0.00001 // 0.000005 //0.000000005
|
||||
},
|
||||
"radius": [0.5, 0],
|
||||
"resolution": 1024,
|
||||
|
||||
@ -8,8 +8,8 @@
|
||||
"physics": {
|
||||
"mass": 0,
|
||||
// "inertia": false,
|
||||
// "type": "bounding box"
|
||||
"type": "mesh"
|
||||
"type": "bounding box"
|
||||
// "type": "mesh"
|
||||
// "type": "hull"
|
||||
}
|
||||
}
|
||||
|
||||
68
bin/data/entities/scripts/ambient_generic.lua
Normal file
68
bin/data/entities/scripts/ambient_generic.lua
Normal file
@ -0,0 +1,68 @@
|
||||
local ent = ent
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local metadataVale = metadata["valve"] or {}
|
||||
|
||||
local soundFile = metadataVale["message"] or ""
|
||||
local flags = metadataVale["spawnflags"] or 0
|
||||
|
||||
local volume = tonumber(metadataVale["health"]) or 10.0
|
||||
volume = volume / 10.0
|
||||
|
||||
local playEverywhere = (math.floor(flags / 1) % 2) ~= 0
|
||||
local startSilent = (math.floor(flags / 16) % 2) ~= 0
|
||||
local isNotLooped = (math.floor(flags / 32) % 2) ~= 0
|
||||
|
||||
local isPlaying = false
|
||||
|
||||
local function playSound()
|
||||
if isPlaying or soundFile == "" then return end
|
||||
isPlaying = true
|
||||
|
||||
local url = "valve://sound/" .. soundFile
|
||||
|
||||
local payload = {
|
||||
filename = string.resolveURI(url, metadata["system"]["root"]),
|
||||
spatial = not playEverywhere,
|
||||
streamed = true,
|
||||
volume = volume,
|
||||
unique = true
|
||||
}
|
||||
payload["wants loops"] = not isNotLooped
|
||||
ent:callHook("sound:Emit.%UID%", payload)
|
||||
end
|
||||
|
||||
local function stopSound()
|
||||
if not isPlaying or soundFile == "" then return end
|
||||
isPlaying = false
|
||||
|
||||
local url = "valve://sound/" .. soundFile
|
||||
ent:callHook("sound:Stop.%UID%", {
|
||||
filename = string.resolveURI(url, metadata["system"]["root"])
|
||||
})
|
||||
end
|
||||
|
||||
ent:addHook("io:Input.%UID%", function(payload)
|
||||
local input = payload.input
|
||||
|
||||
if input == "PlaySound" then
|
||||
playSound()
|
||||
elseif input == "StopSound" then
|
||||
stopSound()
|
||||
elseif input == "ToggleSound" then
|
||||
if isPlaying then
|
||||
stopSound()
|
||||
else
|
||||
playSound()
|
||||
end
|
||||
elseif input == "Volume" then
|
||||
local newVol = tonumber(payload.parameter)
|
||||
if newVol then
|
||||
volume = newVol / 10.0
|
||||
-- to-do: update volume of currently playing sound
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if not startSilent then
|
||||
playSound()
|
||||
end
|
||||
@ -4,27 +4,69 @@ local controller = entities.controller()
|
||||
|
||||
local timer = Timer.new()
|
||||
if not timer:running() then
|
||||
timer:start();
|
||||
timer:start()
|
||||
end
|
||||
|
||||
local polarity = 1
|
||||
local state = 0
|
||||
local targetAlpha = 1.57
|
||||
local alpha = 0
|
||||
local target = Vector3f(0,0,0)
|
||||
local currentDistance = 0
|
||||
local polarity = 1
|
||||
|
||||
local transform = ent:getComponent("Transform")
|
||||
local physicsBody = ent:getComponent("PhysicsBody")
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local metadataDoor = metadata["door"] or {}
|
||||
|
||||
local speed = metadata["speed"] or 1.0
|
||||
local normal = Vector3f(0,0,-1)
|
||||
if metadata["normal"] ~= nil then
|
||||
local sign = -1
|
||||
if metadata["angle"] < 0 then sign = 1 end
|
||||
normal = Vector3f( metadata["normal"][1] * sign, metadata["normal"][2] * sign, metadata["normal"][3] * sign ):normalize()
|
||||
local speed = metadataDoor["speed"] or 100.0
|
||||
local wait = metadataDoor["wait"] or 4.0
|
||||
local isRotating = (metadataDoor["axis"] ~= nil)
|
||||
|
||||
local targetDistance = 0
|
||||
local moveDir = Vector3f(0, 0, 0)
|
||||
local rotAxis = Vector3f(0, 1, 0)
|
||||
local flags = metadataDoor["spawnflags"] or 0
|
||||
|
||||
local isToggle = (math.floor(flags / 32) % 2) ~= 0
|
||||
if isToggle then
|
||||
wait = -1
|
||||
end
|
||||
|
||||
if isRotating then
|
||||
local ax = metadataDoor["axis"]
|
||||
rotAxis = Vector3f(ax[1], ax[2], ax[3]):normalize()
|
||||
|
||||
local dist = metadataDoor["distance"] or 90.0
|
||||
|
||||
local isReverse = (math.floor(flags / 2) % 2) ~= 0
|
||||
if isReverse then
|
||||
polarity = -1
|
||||
end
|
||||
|
||||
polarity = polarity * -1
|
||||
|
||||
if dist < 0 then
|
||||
polarity = polarity * -1
|
||||
dist = math.abs(dist)
|
||||
end
|
||||
|
||||
targetDistance = math.rad(dist)
|
||||
speed = math.rad(speed)
|
||||
else
|
||||
local dir = metadataDoor["direction"]
|
||||
if dir then moveDir = Vector3f(dir[1], dir[2], dir[3]):normalize() end
|
||||
|
||||
local lip = metadataDoor["lip"] or 8.0
|
||||
|
||||
if physicsBody:initialized() then
|
||||
local obb = OBB(physicsBody:bounds())
|
||||
local size = obb.extent * 2.0
|
||||
|
||||
local travelSize = math.abs(moveDir:dot(size))
|
||||
targetDistance = travelSize - lip
|
||||
else
|
||||
targetDistance = 96.0 - lip
|
||||
end
|
||||
end
|
||||
|
||||
-- local soundEmitter = ent:loadChild("/sound.json",true)
|
||||
local soundEmitter = ent
|
||||
|
||||
local playSound = function( key, loop )
|
||||
@ -32,92 +74,141 @@ local playSound = function( key, loop )
|
||||
local url = "/door/" .. key .. ".ogg"
|
||||
soundEmitter:queueHook("sound:Emit.%UID%", {
|
||||
filename = string.resolveURI(url, metadata["system"]["root"]),
|
||||
spatial = true,
|
||||
streamed = true,
|
||||
volume = "sfx",
|
||||
loop = loop
|
||||
spatial = true, streamed = true, volume = "sfx", loop = loop
|
||||
}, 0)
|
||||
end
|
||||
local stopSound = function( key )
|
||||
local url = "/door/" .. key .. ".ogg"
|
||||
soundEmitter:queueHook("sound:Stop.%UID%", {
|
||||
filename = string.resolveURI(url, metadata["system"]["root"])
|
||||
}, 0)
|
||||
end
|
||||
local playSoundscape = function( key )
|
||||
local url = "/soundscape/" .. key .. ".ogg"
|
||||
soundEmitter:queueHook("sound:Emit.%UID%", {
|
||||
filename = string.resolveURI(url, metadata["system"]["root"]),
|
||||
spatial = false,
|
||||
volume = "sfx",
|
||||
loop = true,
|
||||
streamed = true
|
||||
}, 0)
|
||||
end
|
||||
local stopSoundscape = function( key )
|
||||
local url = "/soundscape/" .. key .. ".ogg"
|
||||
soundEmitter:queueHook("sound:Stop.%UID%", {
|
||||
filename = string.resolveURI(url, metadata["system"]["root"])
|
||||
}, 0)
|
||||
|
||||
local function toggleDoor( payload )
|
||||
if state == 0 or state == 3 then
|
||||
state = 1
|
||||
playSound("default_move")
|
||||
|
||||
if isRotating and payload.uid ~= nil then
|
||||
local isOneWay = (math.floor(flags / 16) % 2) ~= 0
|
||||
|
||||
if not isOneWay then
|
||||
local user = entities.get( payload.user )
|
||||
if user and physicsBody:initialized() then
|
||||
local userPos = user:getComponent("Transform").position
|
||||
|
||||
local obb = OBB(physicsBody:bounds())
|
||||
local doorCenter = obb.center
|
||||
local hingePos = transform.position
|
||||
|
||||
local doorBlade = doorCenter - hingePos
|
||||
local doorNormal = rotAxis:cross(doorBlade):normalize()
|
||||
|
||||
local delta = userPos - doorCenter
|
||||
|
||||
if doorNormal:dot(delta) > 0 then
|
||||
polarity = 1
|
||||
else
|
||||
polarity = -1
|
||||
end
|
||||
|
||||
local baseDist = metadataDoor["distance"] or 90.0
|
||||
if baseDist < 0 then
|
||||
polarity = polarity * -1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif state == 2 then
|
||||
state = 3
|
||||
playSound("default_move")
|
||||
end
|
||||
end
|
||||
|
||||
-- on tick
|
||||
ent:bind( "tick", function(self)
|
||||
rot = nil
|
||||
local deltaMove = 0
|
||||
local step = time.delta() * speed
|
||||
|
||||
if state == 1 then
|
||||
alpha = alpha + time.delta() * speed
|
||||
rot = Quaternion.axisAngle( Vector3f(0, 1, 0), time.delta() * speed * polarity )
|
||||
deltaMove = step
|
||||
currentDistance = currentDistance + step
|
||||
|
||||
if alpha > targetAlpha then
|
||||
if currentDistance >= targetDistance then
|
||||
deltaMove = deltaMove - (currentDistance - targetDistance)
|
||||
currentDistance = targetDistance
|
||||
state = 2
|
||||
alpha = targetAlpha
|
||||
timer:reset()
|
||||
playSound("default_stop")
|
||||
|
||||
ent:queueHook("io:FireOutput.%UID%", { output = "OnOpen" }, 0)
|
||||
end
|
||||
elseif state == 3 then
|
||||
deltaMove = -step
|
||||
currentDistance = currentDistance - step
|
||||
|
||||
end
|
||||
if state == 3 then
|
||||
alpha = alpha - time.delta() * speed
|
||||
rot = Quaternion.axisAngle( Vector3f(0, 1, 0), time.delta() * speed * -polarity )
|
||||
|
||||
if alpha < 0 then
|
||||
if currentDistance <= 0 then
|
||||
deltaMove = deltaMove - currentDistance
|
||||
currentDistance = 0
|
||||
state = 0
|
||||
alpha = 0
|
||||
playSound("default_stop")
|
||||
end
|
||||
elseif state == 2 and wait >= 0 then
|
||||
if timer:elapsed() >= wait then
|
||||
state = 3
|
||||
playSound("default_move")
|
||||
end
|
||||
end
|
||||
|
||||
if state > 0 and rot ~= nil then
|
||||
if physicsBody:initialized() then
|
||||
physicsBody:applyRotation( rot )
|
||||
if deltaMove ~= 0 then
|
||||
local finalMove = deltaMove * polarity
|
||||
|
||||
if isRotating then
|
||||
local rot = Quaternion.axisAngle(rotAxis, finalMove)
|
||||
if physicsBody:initialized() then
|
||||
physicsBody:applyRotation(rot)
|
||||
else
|
||||
transform:rotate(rot)
|
||||
end
|
||||
else
|
||||
transform:rotate( rot )
|
||||
local vec = moveDir * finalMove
|
||||
if physicsBody:initialized() then
|
||||
print("Moving by: ", finalMove, " Axis: ", moveDir.x, moveDir.y, moveDir.z)
|
||||
physicsBody:setVelocity(moveDir * (finalMove / time.delta()))
|
||||
else
|
||||
transform.position = transform.position + vec
|
||||
end
|
||||
end
|
||||
else
|
||||
if not isRotating and physicsBody:initialized() then
|
||||
physicsBody:setVelocity(Vector3f(0,0,0))
|
||||
end
|
||||
end
|
||||
end )
|
||||
|
||||
-- on use
|
||||
ent:addHook( "entity:Use.%UID%", function( payload )
|
||||
if payload.user == ent:uid() then return end
|
||||
|
||||
-- if timer:elapsed() <= 0.125 then return end
|
||||
-- timer:reset()
|
||||
local useOpens = (math.floor(flags / 256) % 2) ~= 0
|
||||
|
||||
print("Processing use: " .. ent:name() .. " | " .. payload["depth"] )
|
||||
if useOpens then
|
||||
toggleDoor( payload )
|
||||
|
||||
if state == 0 or state == 3 then
|
||||
state = 1
|
||||
playSound("default_move")
|
||||
if payload.uid ~= nil then
|
||||
local user = entities.get( payload.user )
|
||||
local userTransform = user:getComponent("Transform")
|
||||
local delta = transform.position - userTransform.position
|
||||
local side = normal:dot(delta)
|
||||
if side > 0 then
|
||||
polarity = -1
|
||||
elseif side < 0 then
|
||||
polarity = 1
|
||||
end
|
||||
end
|
||||
elseif state == 2 --[[or state == 1]] then
|
||||
state = 3
|
||||
playSound("default_move")
|
||||
ent:queueHook("io:FireOutput.%UID%", { output = "OnUse" }, 0)
|
||||
else
|
||||
playSound("default_locked")
|
||||
end
|
||||
end )
|
||||
end )
|
||||
|
||||
ent:addHook("io:Input.%UID%", function( payload )
|
||||
local input = payload.input
|
||||
print(ent:name() .. " received I/O Input: " .. input)
|
||||
|
||||
local mockPayload = {
|
||||
user = payload.caller,
|
||||
uid = ent:uid()
|
||||
}
|
||||
|
||||
if input == "Open" and (state == 0 or state == 3) then
|
||||
toggleDoor(mockPayload)
|
||||
elseif input == "Close" and (state == 2 or state == 1) then
|
||||
toggleDoor(mockPayload)
|
||||
elseif input == "Toggle" or input == "Use" then
|
||||
toggleDoor(mockPayload)
|
||||
end
|
||||
end)
|
||||
143
bin/data/entities/scripts/io.lua
Normal file
143
bin/data/entities/scripts/io.lua
Normal file
@ -0,0 +1,143 @@
|
||||
local ent = ent
|
||||
local scene = entities.currentScene()
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
|
||||
-- assign targets
|
||||
_G.IOTargets = _G.IOTargets or {}
|
||||
|
||||
local metadataValve = metadata["valve"] or {}
|
||||
if metadataValve["targetname"] then
|
||||
local tname = metadataValve["targetname"]
|
||||
if not _G.IOTargets[tname] then
|
||||
_G.IOTargets[tname] = {}
|
||||
end
|
||||
|
||||
local uid = ent:uid()
|
||||
local exists = false
|
||||
for _, id in ipairs(_G.IOTargets[tname]) do
|
||||
if id == uid then exists = true break end
|
||||
end
|
||||
if not exists then
|
||||
table.insert(_G.IOTargets[tname], uid)
|
||||
end
|
||||
end
|
||||
|
||||
ent:addHook("io:Input.%UID%", function(payload)
|
||||
local input = payload.input
|
||||
local param = payload.parameter
|
||||
|
||||
-- kill
|
||||
if input == "Kill" or input == "KillHierarchy" then
|
||||
entities.destroy(ent)
|
||||
-- relays
|
||||
elseif input == "FireUser1" then
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnUser1" })
|
||||
elseif input == "FireUser2" then
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnUser2" })
|
||||
elseif input == "FireUser3" then
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnUser3" })
|
||||
elseif input == "FireUser4" then
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnUser4" })
|
||||
-- colors
|
||||
elseif input == "Alpha" then
|
||||
local alphaVal = tonumber(param) / 255.0
|
||||
if alphaVal then
|
||||
-- to-do: apply to instance / material
|
||||
end
|
||||
elseif input == "Color" then
|
||||
local r, g, b = param:match("(%d+) (%d+) (%d+)")
|
||||
-- to-do: apply to instance / material
|
||||
else
|
||||
--print("I/O [(".. ent:uid() ..") " .. ent:name() .. "]: Received Input '" .. input .. "' with param '" .. tostring(param) .. "'")
|
||||
end
|
||||
end )
|
||||
|
||||
-- no connections defined, bail early
|
||||
local connections = metadata["connections"]
|
||||
if not connections then return end
|
||||
|
||||
-- define connections
|
||||
local timer = Timer.new()
|
||||
if not timer:running() then
|
||||
timer:start()
|
||||
end
|
||||
local timesFired = {}
|
||||
for i = 1, #connections do
|
||||
timesFired[i] = 0
|
||||
end
|
||||
|
||||
local pendingOutputs = {}
|
||||
|
||||
ent:bind("tick", function(self)
|
||||
local currentTime = timer:elapsed()
|
||||
|
||||
for i = #pendingOutputs, 1, -1 do
|
||||
local job = pendingOutputs[i]
|
||||
if currentTime >= job.fireTime then
|
||||
|
||||
local targetUIDs = _G.IOTargets[job.target]
|
||||
if targetUIDs then
|
||||
for _, targetUID in ipairs(targetUIDs) do
|
||||
local targetEnt = entities.get(targetUID)
|
||||
if targetEnt:uid() then
|
||||
targetEnt:callHook("io:Input." .. targetUID, {
|
||||
input = job.input,
|
||||
parameter = job.parameter,
|
||||
caller = ent:uid()
|
||||
})
|
||||
end
|
||||
end
|
||||
else
|
||||
print("I/O Warning: Targetname '" .. job.target .. "' not found!")
|
||||
end
|
||||
|
||||
table.remove(pendingOutputs, i)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
ent:addHook("io:FireOutput.%UID%", function(payload)
|
||||
local outputName = payload.output
|
||||
|
||||
for i = 1, #connections do
|
||||
local conn = connections[i]
|
||||
|
||||
if conn.output == outputName then
|
||||
local limit = conn.times or -1
|
||||
|
||||
if limit == -1 or timesFired[i] < limit then
|
||||
timesFired[i] = timesFired[i] + 1
|
||||
|
||||
local delay = conn.delay or 0.0
|
||||
table.insert(pendingOutputs, {
|
||||
fireTime = timer:elapsed() + delay,
|
||||
target = conn.target,
|
||||
input = conn.input,
|
||||
parameter = conn.parameter
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
ent:addHook("entity:Destroy.%UID%", function()
|
||||
if metadataValve["targetname"] then
|
||||
local tname = metadataValve["targetname"]
|
||||
if _G.IOTargets[tname] then
|
||||
for i, id in ipairs(_G.IOTargets[tname]) do
|
||||
if id == ent:uid() then
|
||||
table.remove(_G.IOTargets[tname], i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
ent:addHook("entity:Use.%UID%", function(payload)
|
||||
if payload.user == ent:uid() then return end
|
||||
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnPlayerUse" })
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnUse" })
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnPressed" })
|
||||
end)
|
||||
@ -11,16 +11,27 @@
|
||||
// exact matches
|
||||
"worldspawn": {
|
||||
"physics": { "type": "mesh", "static": true, "mass": 0 },
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true }
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
|
||||
"unwrap mesh": true
|
||||
},
|
||||
"info_player_start": { "action": "attach", "filename": "./player.json", "transform": { "orientation": [ 0, 1, 0, 0 ] } },
|
||||
|
||||
"light_environment": { "ignore": false, "light": {
|
||||
// "color": [0.1, 0.1, 0.1],
|
||||
"power": 1000,
|
||||
"global": true,
|
||||
"bias": {
|
||||
"constant": 0,
|
||||
"slope": 5,
|
||||
"shader": 0.000025
|
||||
},
|
||||
"radius": [0.9999999, 0],
|
||||
"resolution": 2048
|
||||
} },
|
||||
// "/^light_[^e]/": { "ignore": true },
|
||||
|
||||
// regexp matches
|
||||
// "/^worldspawn_/": { "physics": { "type": "mesh", "static": true } },
|
||||
"/^func_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
|
||||
// "/^prop_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
|
||||
"/^prop_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
|
||||
"/^prop_static/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^prop_dynamic/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
41
bin/data/scenes/sourceengine-deprecated/mds_mcdonalds.json
Normal file
41
bin/data/scenes/sourceengine-deprecated/mds_mcdonalds.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"import": "./base_sourceengine.json",
|
||||
"assets": [
|
||||
// { "filename": "./models/mds_mcdonalds.glb" }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" }
|
||||
// ,{ "filename": "/ball.json", "delay": 1 }
|
||||
// ,{ "filename": "/ragdoll.json", "delay": 1 }
|
||||
// ,{ "filename": "/craeture.json", "delay": 2.0 }
|
||||
// ,{ "filename": "/cesiumMan.json", "delay": 2.0 }
|
||||
],
|
||||
"metadata": {
|
||||
"graph": {
|
||||
"bgm": "./audio/soundscape/sh2_ambience.wav",
|
||||
"tags": {
|
||||
// exact matches
|
||||
"func_door_rotating_5473": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5509": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
|
||||
// "prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
|
||||
|
||||
// regex matches
|
||||
"/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": {
|
||||
"base": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||
"emissive": [ 0.0, 0.0, 0.0, 0.0 ],
|
||||
"fMetallic": 0.0,
|
||||
"fRoughness": 0.10000000149011612,
|
||||
"fOcclusion": 1.0,
|
||||
"fAlphaCutoff": 0.5,
|
||||
"iAlbedo": 27,
|
||||
"modeAlpha": 1
|
||||
} },
|
||||
"/alpha_mtl/": { "material": { "modeAlpha": 1 } },
|
||||
"/offwndwb/": { "material": { "modeAlpha": 1 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,13 +14,7 @@
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
|
||||
"unwrap mesh": true
|
||||
},
|
||||
"worldspawn_skybox": {
|
||||
"physics": { "type": "mesh", "static": true, "mass": 0 },
|
||||
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
|
||||
"unwrap mesh": true
|
||||
},
|
||||
"info_player_spawn": { "action": "attach", "filename": "./player.json", "transform": { "orientation": [ 0, 1, 0, 0 ] } },
|
||||
"light_environment": { "ignore": false, "light": {
|
||||
/*"light_environment": { "ignore": false, "light": {
|
||||
// "color": [0.1, 0.1, 0.1],
|
||||
"power": 1000,
|
||||
"global": true,
|
||||
@ -31,15 +25,17 @@
|
||||
},
|
||||
"radius": [0.9999999, 0],
|
||||
"resolution": 2048
|
||||
} },
|
||||
// "/^light_[^e]/": { "ignore": true },
|
||||
} },*/
|
||||
|
||||
// automatically handled
|
||||
// "/^func_door/": { "action": "load", "payload": { "import": "/door.json" } },
|
||||
// "/^prop_door/": { "action": "load", "payload": { "import": "/door.json" } },
|
||||
|
||||
"/ambient_generic/": { "action": "load", "payload": { "import": "/ambient_generic.json" } },
|
||||
|
||||
// regexp matches
|
||||
// "/^worldspawn_/": { "physics": { "type": "mesh", "static": true } },
|
||||
"/^func_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
|
||||
"/^prop_door_/": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [-1,0,0] } } },
|
||||
"/^prop_static/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^prop_dynamic/": { /*"action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } }*/ },
|
||||
"/^prop_static/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
"/^prop_dynamic/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
"/^prop_physics/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
|
||||
|
||||
@ -1,40 +1,15 @@
|
||||
{
|
||||
"import": "./base_sourceengine.json",
|
||||
"assets": [
|
||||
// { "filename": "./models/mds_mcdonalds.glb" }
|
||||
// { "filename": "./models/mds_mcdonalds.bsp" }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" }
|
||||
// ,{ "filename": "/ball.json", "delay": 1 }
|
||||
// ,{ "filename": "/ragdoll.json", "delay": 1 }
|
||||
// ,{ "filename": "/craeture.json", "delay": 2.0 }
|
||||
// ,{ "filename": "/cesiumMan.json", "delay": 2.0 }
|
||||
],
|
||||
"metadata": {
|
||||
"graph": {
|
||||
"bgm": "./audio/soundscape/sh2_ambience.wav",
|
||||
// "bgm": "./audio/soundscape/sh2_ambience.wav",
|
||||
// realistically shouldn't ever need to define additional tags for BSPs
|
||||
"tags": {
|
||||
// exact matches
|
||||
"func_door_rotating_5473": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5509": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
"func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
|
||||
|
||||
// "prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
|
||||
|
||||
// regex matches
|
||||
"/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": {
|
||||
"base": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||
"emissive": [ 0.0, 0.0, 0.0, 0.0 ],
|
||||
"fMetallic": 0.0,
|
||||
"fRoughness": 0.10000000149011612,
|
||||
"fOcclusion": 1.0,
|
||||
"fAlphaCutoff": 0.5,
|
||||
"iAlbedo": 27,
|
||||
"modeAlpha": 1
|
||||
} },
|
||||
"/alpha_mtl/": { "material": { "modeAlpha": 1 } },
|
||||
"/offwndwb/": { "material": { "modeAlpha": 1 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
{
|
||||
"import": "./base_sourceengine.json",
|
||||
"assets": [
|
||||
{ "filename": "./models/mds_mcdonalds.bsp" }
|
||||
// { "filename": "./models/mds_mcdonalds/graph.json" }
|
||||
],
|
||||
"metadata": {
|
||||
"graph": {
|
||||
// "bgm": "./audio/soundscape/sh2_ambience.wav",
|
||||
"tags": {
|
||||
// regex matches
|
||||
"/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": {
|
||||
"base": [ 1.0, 1.0, 1.0, 1.0 ],
|
||||
"emissive": [ 0.0, 0.0, 0.0, 0.0 ],
|
||||
"fMetallic": 0.0,
|
||||
"fRoughness": 0.10000000149011612,
|
||||
"fOcclusion": 1.0,
|
||||
"fAlphaCutoff": 0.5,
|
||||
"iAlbedo": 27,
|
||||
"modeAlpha": 1
|
||||
} },
|
||||
"/alpha_mtl/": { "material": { "modeAlpha": 1 } },
|
||||
"/offwndwb/": { "material": { "modeAlpha": 1 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,8 @@ namespace impl {
|
||||
return pod::Vector3f{ -vertex.y, vertex.z, vertex.x } * scale;
|
||||
}
|
||||
|
||||
ext::json::Value processValue( const uf::stl::string& v );
|
||||
uf::stl::string readString( std::ifstream& file );
|
||||
bool parseKeyValue( const uf::stl::string& line, uf::stl::string& key, uf::stl::string& value );
|
||||
pod::Material& addMaterial( pod::Graph& graph, const uf::stl::string& name );
|
||||
}
|
||||
@ -94,6 +94,8 @@ namespace uf {
|
||||
|
||||
const uf::stl::string& getFilename() const;
|
||||
float getDuration() const;
|
||||
|
||||
bool hasLoops() const;
|
||||
};
|
||||
namespace audio {
|
||||
extern uf::Audio null;
|
||||
|
||||
@ -47,7 +47,14 @@ namespace uf {
|
||||
|
||||
uf::Timer<> timer;
|
||||
float elapsed = 0;
|
||||
|
||||
struct {
|
||||
bool has = false;
|
||||
uint32_t start = 0;
|
||||
uint32_t end = 0;
|
||||
} loop;
|
||||
} info;
|
||||
|
||||
struct {
|
||||
bool streamed = true;
|
||||
bool loop = false;
|
||||
|
||||
@ -24,6 +24,7 @@ namespace uf {
|
||||
uf::stl::string UF_API rtrim( const uf::stl::string& );
|
||||
uf::stl::string UF_API trim( const uf::stl::string& );
|
||||
uf::stl::vector<uf::stl::string> UF_API split( const uf::stl::string&, const uf::stl::string& );
|
||||
uf::stl::vector<uf::stl::string> UF_API split( const uf::stl::string&, char );
|
||||
uf::stl::string UF_API si( double value, const uf::stl::string& unit, size_t precision = 3 );
|
||||
bool UF_API contains( const uf::stl::string&, const uf::stl::string& );
|
||||
uf::stl::vector<const char*> UF_API cStrings( const uf::stl::vector<uf::stl::string>& );
|
||||
|
||||
@ -275,7 +275,7 @@ bool uf::asset::has( const uf::asset::Payload& payload ) {
|
||||
}
|
||||
void uf::asset::remove( const uf::stl::string& url ) {
|
||||
if ( !uf::asset::has( url ) ) return;
|
||||
auto& userdata = uf::asset::map[url];
|
||||
auto userdata = std::move(uf::asset::map[url]);
|
||||
#if UF_COMPONENT_POINTERED_USERDATA
|
||||
if ( userdata.data ) uf::pointeredUserdata::destroy( userdata );
|
||||
#else
|
||||
@ -287,7 +287,7 @@ uf::asset::userdata_t& uf::asset::get( const uf::stl::string& url ) {
|
||||
return uf::asset::map[url];
|
||||
}
|
||||
uf::asset::userdata_t uf::asset::release( const uf::stl::string& url ) {
|
||||
auto userdata = uf::asset::get( url );
|
||||
auto userdata = std::move(uf::asset::get( url ));
|
||||
uf::asset::map.erase( url );
|
||||
return userdata;
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ void ext::SoundEmitterBehavior::initialize( uf::Object& self ) {
|
||||
for ( auto& audio : sounds ) {
|
||||
if ( audio.getFilename() != filename ) continue;
|
||||
audio.stop();
|
||||
audio.destroy();
|
||||
// audio.destroy();
|
||||
}
|
||||
});
|
||||
this->addHook( "sound:Emit.%UID%", [&]( pod::PCM& waveform ){
|
||||
@ -61,8 +61,11 @@ void ext::SoundEmitterBehavior::initialize( uf::Object& self ) {
|
||||
if ( json["gain"].is<double>() ) audio.setGain(json["gain"].as<float>());
|
||||
if ( json["rolloffFactor"].is<double>() ) audio.setRolloffFactor(json["rolloffFactor"].as<float>());
|
||||
if ( json["maxDistance"].is<double>() ) audio.setMaxDistance(json["maxDistance"].as<float>());
|
||||
if ( json["loop"].is<bool>() ) audio.loop(json["loop"].as<bool>());
|
||||
if ( json["spatial"].is<bool>() ) audio.setSpatial(json["spatial"].as<bool>());
|
||||
if ( json["loop"].is<bool>() ) audio.loop(json["loop"].as<bool>());
|
||||
if ( json["wants loop"].is<bool>() ) {
|
||||
audio.loop(json["wants loop"].as<bool>(true) && audio.hasLoops());
|
||||
}
|
||||
|
||||
float volume = 1.0f;
|
||||
if ( json["volume"].is<double>() ) volume = json["volume"].as<float>();
|
||||
|
||||
@ -96,6 +96,7 @@ namespace {
|
||||
material.indexEmissive = json["iEmissive"].as(material.indexEmissive);
|
||||
material.indexOcclusion = json["iOcclusion"].as(material.indexOcclusion);
|
||||
material.indexMetallicRoughness = json["iMetallicRoughness"].as(material.indexMetallicRoughness);
|
||||
material.modeCull = json["modeCull"].as(material.modeCull);
|
||||
material.modeAlpha = json["modeAlpha"].as(material.modeAlpha);
|
||||
|
||||
return material;
|
||||
@ -166,6 +167,7 @@ namespace {
|
||||
instance.materialID = json["materialID"].as( instance.materialID );
|
||||
instance.primitiveID = json["primitiveID"].as( instance.primitiveID );
|
||||
instance.meshID = json["meshID"].as( instance.meshID );
|
||||
instance.lightmapID = json["lightmapID"].as( instance.lightmapID );
|
||||
instance.auxID = json["auxID"].as( instance.auxID );
|
||||
instance.objectID = json["objectID"].as( instance.objectID );
|
||||
instance.bounds.min = uf::vector::decode( json["bounds"]["min"], instance.bounds.min );
|
||||
|
||||
@ -58,6 +58,7 @@ namespace {
|
||||
if ( material.indexEmissive >= 0 ) json["iEmissive"] = material.indexEmissive;
|
||||
if ( material.indexOcclusion >= 0 ) json["iOcclusion"] = material.indexOcclusion;
|
||||
if ( material.indexMetallicRoughness >= 0 ) json["iMetallicRoughness"] = material.indexMetallicRoughness;
|
||||
json["modeCull"] = material.modeCull;
|
||||
json["modeAlpha"] = material.modeAlpha;
|
||||
return json;
|
||||
}
|
||||
@ -115,6 +116,7 @@ namespace {
|
||||
json["materialID"] = instance.materialID;
|
||||
json["primitiveID"] = instance.primitiveID;
|
||||
json["meshID"] = instance.meshID;
|
||||
json["lightmapID"] = instance.lightmapID;
|
||||
json["auxID"] = instance.auxID;
|
||||
json["objectID"] = instance.objectID;
|
||||
|
||||
|
||||
@ -768,7 +768,7 @@ void uf::graph::process( pod::Graph& graph ) {
|
||||
// root entity should already be bound, but just in case
|
||||
if ( !graph.root.entity ) {
|
||||
graph.root.entity = new uf::Object;
|
||||
UF_MSG_DEBUG("binding root: {}", (void*) graph.root.entity);
|
||||
// UF_MSG_DEBUG("binding root: {}", (void*) graph.root.entity);
|
||||
}
|
||||
|
||||
// copy lighting settings from graph
|
||||
@ -952,8 +952,7 @@ void uf::graph::process( pod::Graph& graph ) {
|
||||
image.clear();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// process nodes
|
||||
UF_DEBUG_TIMER_MULTITRACE("Processing nodes");
|
||||
@ -964,6 +963,29 @@ void uf::graph::process( pod::Graph& graph ) {
|
||||
if ( node.entity ) UF_DEBUG_TIMER_MULTITRACE("Processed node: {}", node.name);
|
||||
}
|
||||
|
||||
// spawn player if not already spawned
|
||||
if ( auto* player = scene.findByName("Player"); !player ) {
|
||||
int32_t spawnID = -1;
|
||||
uf::stl::vector<int32_t> spawns;
|
||||
for ( auto nodeID = 0; nodeID < graph.nodes.size(); ++nodeID ) {
|
||||
auto& node = graph.nodes[nodeID];
|
||||
if ( node.name == "info_player_start" ) {
|
||||
spawnID = nodeID;
|
||||
} else if ( node.name.starts_with("info_player_") ) {
|
||||
spawns.emplace_back(nodeID);
|
||||
}
|
||||
}
|
||||
if ( spawnID == -1 && !spawns.empty() ) spawnID = uf::stl::random( spawns );
|
||||
if ( spawnID != -1 ) {
|
||||
auto& node = graph.nodes[spawnID];
|
||||
auto& child = /*graph.root.entity->*/node.entity->loadChild( "./player.json", false ); // to-do: do not hardcode this
|
||||
auto& childTransform = child.getComponent<pod::Transform<>>();
|
||||
|
||||
auto flatten = uf::transform::flatten( node.transform );
|
||||
childTransform = flatten;
|
||||
}
|
||||
}
|
||||
|
||||
// patch materials/textures
|
||||
UF_DEBUG_TIMER_MULTITRACE("Patching textures/materials");
|
||||
for ( auto& name : graph.materials ) {
|
||||
@ -1205,6 +1227,22 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
metadataJson["system"]["graph"]["name"] = node.name;
|
||||
metadataJson["system"]["graph"]["index"] = index;
|
||||
|
||||
uf::Serializer loadJson;
|
||||
// convert metadata["valve"] into internal values:
|
||||
auto& metadataValve = node.metadata["valve"];
|
||||
if ( ext::json::isObject( metadataValve ) ) {
|
||||
// bind door script
|
||||
if ( ext::json::isObject( metadataValve["door"] ) ) {
|
||||
node.metadata["door"] = metadataValve["door"];
|
||||
loadJson["imports"].emplace_back("/door.json");
|
||||
}
|
||||
// bind io connectivity
|
||||
if ( ext::json::isArray( metadataValve["connections"] ) || metadataValve["targetname"].is<uf::stl::string>() ) {
|
||||
node.metadata["connections"] = metadataValve["connections"];
|
||||
loadJson["imports"].emplace_back("/io.json");
|
||||
}
|
||||
}
|
||||
|
||||
if ( ext::json::isObject( tag ) ) {
|
||||
if ( tag["action"].as<uf::stl::string>() == "load" ) {
|
||||
if ( tag["filename"].is<uf::stl::string>() ) {
|
||||
@ -1213,6 +1251,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
} else if ( ext::json::isObject( tag["payload"] ) ) {
|
||||
uf::Serializer json = tag["payload"];
|
||||
json["root"] = graphMetadataJson["root"];
|
||||
json.import( loadJson );
|
||||
entity.load(json);
|
||||
}
|
||||
} else if ( tag["action"].as<uf::stl::string>() == "attach" ) {
|
||||
@ -1227,8 +1266,14 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
if ( tag["static"].is<bool>() ) {
|
||||
metadata.system.ignoreGraph = tag["static"].as<bool>();
|
||||
}
|
||||
} else if ( ext::json::isObject( loadJson ) ) {
|
||||
loadJson["root"] = graphMetadataJson["root"];
|
||||
entity.load( loadJson );
|
||||
}
|
||||
|
||||
// import metadata
|
||||
metadataJson.import( node.metadata );
|
||||
|
||||
// create as light
|
||||
{
|
||||
// attempt to resolve a light name
|
||||
|
||||
@ -45,13 +45,13 @@ void uf::GraphBehavior::initialize( uf::Object& self ) {
|
||||
if ( !uf::asset::has( payload ) ) uf::asset::load( payload );
|
||||
auto& graph = payload.asComponent ? this->getComponent<pod::Graph>() : uf::asset::get<pod::Graph>( payload );
|
||||
if ( !payload.asComponent ) {
|
||||
this->moveComponent<pod::Graph>( uf::asset::get( payload.filename ) );
|
||||
uf::asset::remove( payload.filename );
|
||||
auto userdata = uf::asset::release( payload.filename );
|
||||
this->moveComponent<pod::Graph>( userdata );
|
||||
}
|
||||
|
||||
// bind graph's root entity to self if different
|
||||
if ( graph.root.entity && graph.root.entity != this ) {
|
||||
UF_MSG_DEBUG("binding root transform to self");
|
||||
//UF_MSG_DEBUG("binding root transform to self");
|
||||
auto& transform = this->getComponent<pod::Transform<>>();
|
||||
auto& root = *graph.root.entity;
|
||||
root.getComponent<pod::Transform<>>().reference = &transform;
|
||||
|
||||
@ -22,20 +22,21 @@ void uf::LuaBehavior::initialize( uf::Object& self ) {
|
||||
this->addHook( "asset:Load.%UID%", [&](pod::payloads::assetLoad& payload){
|
||||
if ( !uf::asset::isExpected( payload, uf::asset::Type::LUA ) ) return;
|
||||
if ( !uf::asset::has( payload ) ) uf::asset::load( payload );
|
||||
auto& script = uf::asset::get<pod::LuaScript>( payload );
|
||||
if ( !payload.asComponent ) {
|
||||
// auto asset = uf::asset::release( payload.filename );
|
||||
// this->moveComponent<pod::LuaScript>( asset );
|
||||
this->moveComponent<pod::LuaScript>( uf::asset::get( payload.filename ) );
|
||||
uf::asset::remove( payload.filename );
|
||||
}
|
||||
/*
|
||||
if ( !uf::asset::has(payload.filename) ) uf::asset::load( payload );
|
||||
auto& script = uf::asset::get<pod::LuaScript>(payload.filename);
|
||||
*/
|
||||
// auto& script = this->getComponent<pod::LuaScript>();
|
||||
|
||||
auto script = uf::asset::get<pod::LuaScript>( payload.filename );
|
||||
script.env["ent"] = &this->as<uf::Object>();
|
||||
ext::lua::run( script );
|
||||
/*
|
||||
// crashes when trying to destroy the moved component (despite it having no reason to get destroyed)
|
||||
auto& script = payload.asComponent ? this->getComponent<pod::LuaScript>() : uf::asset::get<pod::LuaScript>( payload );
|
||||
if ( !payload.asComponent ) {
|
||||
auto userdata = uf::asset::release( payload.filename );
|
||||
this->moveComponent<pod::LuaScript>( userdata );
|
||||
}
|
||||
|
||||
script.env["ent"] = &this->as<uf::Object>();
|
||||
ext::lua::run( script );
|
||||
*/
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -71,6 +71,8 @@ void uf::Object::queueDeletion() {
|
||||
// mark for destruction
|
||||
metadata.system.markedForDeletion = true;
|
||||
uf::instantiator::queueDeletion( *this );
|
||||
// dispatch deletion hooks
|
||||
this->callHook("entity:Destroy.%UID%");
|
||||
}
|
||||
|
||||
uf::Hooks::return_t uf::Object::callHook( const uf::stl::string& name ) {
|
||||
@ -253,7 +255,7 @@ void uf::Object::loadAssets( const uf::Serializer& _json ){
|
||||
|
||||
switch ( assetType ) {
|
||||
case uf::asset::Type::LUA: {
|
||||
// payload.asComponent = true;
|
||||
payload.asComponent = false;
|
||||
if ( bind ) uf::instantiator::bind("LuaBehavior", *this);
|
||||
} break;
|
||||
case uf::asset::Type::GRAPH: {
|
||||
|
||||
@ -47,17 +47,19 @@ namespace {
|
||||
|
||||
int seek( void* userdata, ogg_int64_t to, int type ) {
|
||||
VorbisVfsContext* ctx = (VorbisVfsContext*) userdata;
|
||||
int64_t targetOffset = 0;
|
||||
|
||||
switch ( type ) {
|
||||
case SEEK_CUR: ctx->currentOffset += to; break;
|
||||
case SEEK_END: ctx->currentOffset = ctx->totalSize + to; break;
|
||||
case SEEK_SET: ctx->currentOffset = to; break;
|
||||
case SEEK_CUR: targetOffset = (int64_t)ctx->currentOffset + to; break;
|
||||
case SEEK_END: targetOffset = (int64_t)ctx->totalSize + to; break;
|
||||
case SEEK_SET: targetOffset = to; break;
|
||||
default: return -1;
|
||||
}
|
||||
|
||||
if ((int64_t)ctx->currentOffset < 0) ctx->currentOffset = 0;
|
||||
if (ctx->currentOffset > ctx->totalSize) ctx->currentOffset = ctx->totalSize;
|
||||
if (targetOffset < 0) targetOffset = 0;
|
||||
if ((size_t)targetOffset > ctx->totalSize) targetOffset = ctx->totalSize;
|
||||
|
||||
ctx->currentOffset = (size_t)targetOffset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -130,6 +132,29 @@ void ext::vorbis::open(uf::Audio::Metadata& metadata) {
|
||||
metadata.info.frequency = info->rate;
|
||||
metadata.info.duration = ov_time_total(vorbisFile, -1);
|
||||
|
||||
metadata.info.loop.has = false;
|
||||
metadata.info.loop.start = 0;
|
||||
metadata.info.loop.end = (uint32_t)ov_pcm_total(vorbisFile, -1); // Default to full file
|
||||
|
||||
vorbis_comment* vc = ov_comment(vorbisFile, -1);
|
||||
if ( vc != nullptr ) {
|
||||
for ( auto i = 0; i < vc->comments; ++i ) {
|
||||
uf::stl::string comment(vc->user_comments[i], vc->comment_lengths[i]);
|
||||
uf::stl::string upperComment = uf::string::uppercase( comment );
|
||||
|
||||
// to-do: handle exceptions without exceptions
|
||||
if ( upperComment.starts_with("LOOPSTART=" )) {
|
||||
metadata.info.loop.start = std::stoul(comment.substr(10));
|
||||
metadata.info.loop.has = true;
|
||||
} else if ( upperComment.starts_with("LOOPLENGTH=" )) {
|
||||
uint32_t length = std::stoul(comment.substr(11));
|
||||
metadata.info.loop.end = metadata.info.loop.start + length;
|
||||
} else if ( upperComment.starts_with("LOOPEND=") ) {
|
||||
metadata.info.loop.end = std::stoul(comment.substr(8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !format(metadata, info->channels, 16) ) {
|
||||
ov_clear(vorbisFile);
|
||||
metadata.stream.context = nullptr;
|
||||
@ -154,6 +179,10 @@ void ext::vorbis::load( uf::Audio::Metadata& metadata ) {
|
||||
} while ( read > 0 );
|
||||
|
||||
metadata.al.buffer.buffer(metadata.info.format, bytes.data(), (ALsizei) bytes.size(), metadata.info.frequency);
|
||||
if ( metadata.info.loop.has ) {
|
||||
ALint loopPoints[2] = { (ALint) metadata.info.loop.start, (ALint) metadata.info.loop.end };
|
||||
alBufferiv(metadata.al.buffer.getIndex(), 0x2015 /* AL_LOOP_POINTS_SOFT */, loopPoints);
|
||||
}
|
||||
metadata.al.source.set(AL_BUFFER, (ALint) metadata.al.buffer.getIndex());
|
||||
|
||||
ov_clear(vorbisFile);
|
||||
@ -175,7 +204,8 @@ void ext::vorbis::stream(uf::Audio::Metadata& metadata) {
|
||||
int result = OV_READ(vorbisFile, buffer + totalRead, uf::audio::bufferSize - totalRead, endian, 2, 1, &bitStream);
|
||||
if (result <= 0) {
|
||||
if (result == 0 && metadata.settings.loop) {
|
||||
if (ov_raw_seek(vorbisFile, 0) != 0) {
|
||||
uint32_t seekTarget = metadata.info.loop.has ? metadata.info.loop.start : 0;
|
||||
if ( ov_pcm_seek(vorbisFile, seekTarget) != 0 ) {
|
||||
UF_MSG_ERROR("Vorbis: failed to loop (seek to start): {}", metadata.filename);
|
||||
break;
|
||||
}
|
||||
@ -235,7 +265,9 @@ void ext::vorbis::update(uf::Audio::Metadata& metadata) {
|
||||
int result = OV_READ(vorbisFile, buffer + totalRead, uf::audio::bufferSize - totalRead, endian, 2, 1, &bitStream);
|
||||
if (result <= 0) {
|
||||
if (result == 0 && metadata.settings.loop) {
|
||||
if (ov_raw_seek(vorbisFile, 0) != 0) {
|
||||
uint32_t seekTarget = metadata.info.loop.has ? metadata.info.loop.start : 0;
|
||||
|
||||
if ( ov_pcm_seek(vorbisFile, seekTarget) != 0 ) {
|
||||
UF_MSG_ERROR("Vorbis: failed to loop (seek to start): {}", metadata.filename);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -41,18 +41,18 @@ namespace {
|
||||
|
||||
drwav_bool32 drwav_vfs_seek(void* pUserData, int offset, drwav_seek_origin origin) {
|
||||
DrWavVfsContext* ctx = (DrWavVfsContext*)pUserData;
|
||||
long long targetOffset = 0;
|
||||
|
||||
if ((int)origin == 0) {
|
||||
ctx->currentOffset = (size_t)offset;
|
||||
} else if ((int)origin == 1) {
|
||||
if (offset < 0 && (size_t)(-offset) > ctx->currentOffset) {
|
||||
ctx->currentOffset = 0;
|
||||
} else {
|
||||
ctx->currentOffset += offset;
|
||||
}
|
||||
if ( (int)origin == 0 ) {
|
||||
targetOffset = offset;
|
||||
} else if ( (int)origin == 1 ) {
|
||||
targetOffset = (long long)ctx->currentOffset + offset;
|
||||
}
|
||||
|
||||
if (ctx->currentOffset > ctx->totalSize) ctx->currentOffset = ctx->totalSize;
|
||||
if (targetOffset < 0) targetOffset = 0;
|
||||
if ((size_t)targetOffset > ctx->totalSize) targetOffset = ctx->totalSize;
|
||||
|
||||
ctx->currentOffset = (size_t)targetOffset;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -136,6 +136,23 @@ void ext::wav::open(uf::Audio::Metadata& metadata) {
|
||||
metadata.info.frequency = wav->sampleRate;
|
||||
metadata.info.duration = (double) wav->totalPCMFrameCount / wav->sampleRate;
|
||||
|
||||
for ( drwav_uint32 i = 0; i < wav->metadataCount; ++i ) {
|
||||
if ( wav->pMetadata[i].type == drwav_metadata_type_smpl ) {
|
||||
const drwav_smpl& smpl = wav->pMetadata[i].data.smpl;
|
||||
|
||||
if ( smpl.sampleLoopCount > 0 && smpl.pLoops != nullptr ) {
|
||||
metadata.info.loop.has = true;
|
||||
metadata.info.loop.start = smpl.pLoops[0].firstSampleOffset;
|
||||
metadata.info.loop.end = smpl.pLoops[0].lastSampleOffset;
|
||||
|
||||
if ( metadata.info.loop.end == 0 ) {
|
||||
metadata.info.loop.end = (uint32_t)wav->totalPCMFrameCount;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// determine OpenAL format
|
||||
if (wav->channels == 1 && wav->bitsPerSample == 8)
|
||||
metadata.info.format = AL_FORMAT_MONO8;
|
||||
@ -168,6 +185,10 @@ void ext::wav::load(uf::Audio::Metadata& metadata) {
|
||||
}
|
||||
|
||||
metadata.al.buffer.buffer(metadata.info.format, bytes.data(), (ALsizei) bytes.size(), metadata.info.frequency);
|
||||
if ( metadata.info.loop.has ) {
|
||||
ALint loopPoints[2] = { (ALint) metadata.info.loop.start, (ALint) metadata.info.loop.end };
|
||||
alBufferiv(metadata.al.buffer.getIndex(), 0x2015 /* AL_LOOP_POINTS_SOFT */, loopPoints);
|
||||
}
|
||||
metadata.al.source.set(AL_BUFFER, (ALint) metadata.al.buffer.getIndex());
|
||||
|
||||
funs::close(&metadata);
|
||||
@ -186,7 +207,7 @@ void ext::wav::stream(uf::Audio::Metadata& metadata) {
|
||||
if (bytesRead == 0) {
|
||||
if (metadata.settings.loop) {
|
||||
metadata.stream.consumed = 0;
|
||||
if (funs::seek(&metadata, 0, SEEK_SET) != 0) {
|
||||
if ( funs::seek(&metadata, metadata.info.loop.start, SEEK_SET) != 0 ) {
|
||||
UF_MSG_ERROR("WAV: failed to loop (seek to start): {}", metadata.filename);
|
||||
break;
|
||||
}
|
||||
@ -238,7 +259,7 @@ void ext::wav::update(uf::Audio::Metadata& metadata) {
|
||||
if (bytesRead == 0) {
|
||||
// no more data left to read, reset file stream if we're looping
|
||||
if (!metadata.settings.loop) break;
|
||||
if (funs::seek(&metadata, 0, SEEK_SET) != 0) {
|
||||
if ( funs::seek(&metadata, metadata.info.loop.start, SEEK_SET) != 0 ) {
|
||||
UF_MSG_ERROR("WAV: failed to loop (seek to start): {}", metadata.filename);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -161,20 +161,42 @@ namespace binds {
|
||||
if ( !functionPointer ) return false;
|
||||
pod::Behavior::function_t& function = *functionPointer;
|
||||
|
||||
bool hasExisting = (bool)(function); // check if a function is already bound to this slot
|
||||
auto prev = function; // copy the existing function
|
||||
|
||||
#if !UF_LUA_PCALLS
|
||||
function = fun;
|
||||
if ( hasExisting ) {
|
||||
function = [prev, fun]( uf::Object& s ) {
|
||||
prev(s);
|
||||
fun(s);
|
||||
};
|
||||
} else {
|
||||
function = fun;
|
||||
}
|
||||
#else
|
||||
function = [fun]( uf::Object& s ) {
|
||||
auto result = fun(s);
|
||||
if ( !result.valid() ) {
|
||||
sol::error err = result;
|
||||
UF_MSG_ERROR("{}", err.what());
|
||||
}
|
||||
};
|
||||
if ( hasExisting ) {
|
||||
function = [prev, fun]( uf::Object& s ) {
|
||||
prev(s); // call the previous script's tick
|
||||
auto result = fun(s); // call the new script's tick
|
||||
if ( !result.valid() ) {
|
||||
sol::error err = result;
|
||||
UF_MSG_ERROR("{}", err.what());
|
||||
}
|
||||
};
|
||||
} else {
|
||||
function = [fun]( uf::Object& s ) {
|
||||
auto result = fun(s);
|
||||
if ( !result.valid() ) {
|
||||
sol::error err = result;
|
||||
UF_MSG_ERROR("{}", err.what());
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
self.generateGraph();
|
||||
return true;
|
||||
}
|
||||
|
||||
uf::Object& findByUid( uf::Object& self, size_t index ) {
|
||||
auto* pointer = self.findByUid( index );
|
||||
if ( pointer ) return pointer->as<uf::Object>();
|
||||
|
||||
@ -49,6 +49,10 @@ namespace binds {
|
||||
return std::make_tuple( object, depth );
|
||||
}
|
||||
|
||||
pod::AABB bounds( const pod::PhysicsBody& self ) {
|
||||
return self.bounds;
|
||||
}
|
||||
|
||||
pod::PhysicsBody& asAabb( pod::PhysicsBody& self, const pod::AABB& shape ) {
|
||||
return uf::physics::initialize( self, shape );
|
||||
}
|
||||
@ -140,10 +144,16 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::OBB,
|
||||
return self = copy;
|
||||
},
|
||||
#if OBB_EXTENT_CENTER
|
||||
[]( pod::OBB& self, const pod::AABB& copy ) {
|
||||
return self = pod::OBB{ .extent = (copy.max - copy.min) * 0.5f, .center = (copy.max + copy.min) * 0.5f };
|
||||
},
|
||||
[]( pod::OBB& self, const pod::Vector3f& extent, const pod::Vector3f& center ) {
|
||||
return self = pod::OBB{ .extent = extent, .center = center };
|
||||
}
|
||||
#else
|
||||
[]( pod::OBB& self, const pod::AABB& copy ) {
|
||||
return self = pod::OBB{ .center = (copy.max + copy.min) * 0.5f, .extent = (copy.max - copy.min) * 0.5f };
|
||||
},
|
||||
[]( pod::OBB& self, const pod::Vector3f& center, const pod::Vector3f& extent ) {
|
||||
return self = pod::OBB{ .center = center, .extent = extent };
|
||||
}
|
||||
@ -235,6 +245,8 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::PhysicsBody,
|
||||
UF_LUA_REGISTER_USERTYPE_DEFINE( getMass, UF_LUA_C_FUN(::binds::body::getMass) ),
|
||||
UF_LUA_REGISTER_USERTYPE_DEFINE( setMass, UF_LUA_C_FUN(::binds::body::setMass) ),
|
||||
|
||||
UF_LUA_REGISTER_USERTYPE_DEFINE( bounds, UF_LUA_C_FUN(::binds::body::bounds) ),
|
||||
|
||||
UF_LUA_REGISTER_USERTYPE_DEFINE( asAabb, UF_LUA_C_FUN(::binds::body::asAabb) ),
|
||||
UF_LUA_REGISTER_USERTYPE_DEFINE( asObb, UF_LUA_C_FUN(::binds::body::asObb) ),
|
||||
UF_LUA_REGISTER_USERTYPE_DEFINE( asSphere, UF_LUA_C_FUN(::binds::body::asSphere) ),
|
||||
|
||||
@ -201,6 +201,30 @@ namespace impl {
|
||||
pod::Atlas lightmapAtlas;
|
||||
};
|
||||
|
||||
struct BSP_IO {
|
||||
uf::stl::string output;
|
||||
uf::stl::string target;
|
||||
uf::stl::string input;
|
||||
uf::stl::string parameter;
|
||||
float delay;
|
||||
int timesToFire;
|
||||
};
|
||||
|
||||
impl::BSP_IO parseIO( const uf::stl::string& output, const uf::stl::string& value ) {
|
||||
char delimiter = value.find('\x1B') != uf::stl::string::npos ? '\x1B' : ','; // either ESC or a comma
|
||||
auto tokens = uf::string::split( value, delimiter );
|
||||
|
||||
impl::BSP_IO io;
|
||||
io.output = output;
|
||||
io.target = tokens.size() > 0 ? tokens[0] : "";
|
||||
io.input = tokens.size() > 1 ? tokens[1] : "";
|
||||
io.parameter = tokens.size() > 2 ? tokens[2] : "";
|
||||
io.delay = tokens.size() > 3 ? std::stof(tokens[3]) : 0.0f;
|
||||
io.timesToFire = tokens.size() > 4 ? std::stoi(tokens[4]) : -1;
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
pod::Atlas::hash_t faceHash( size_t i ) {
|
||||
return ::fmt::format("face_{}", i);
|
||||
}
|
||||
@ -403,12 +427,9 @@ namespace impl {
|
||||
}
|
||||
|
||||
// parse angles
|
||||
// to-do: fix oddities
|
||||
auto angles = metadata["angles"].as<uf::stl::string>("");
|
||||
if ( angles != "" ) {
|
||||
auto pyr = impl::str2vec<pod::Vector3f>( angles ) * DEG_2_RAD;
|
||||
pyr.x = -pyr.x;
|
||||
|
||||
auto pyr = impl::str2vec<pod::Vector3f>( angles ) * -DEG_2_RAD;
|
||||
node.transform.orientation = uf::quaternion::euler( pyr );
|
||||
}
|
||||
|
||||
@ -456,13 +477,45 @@ namespace impl {
|
||||
light.intensity *= 0.2f; // scale down
|
||||
}
|
||||
|
||||
// parse player spawn info
|
||||
if ( classname == "info_player_start" ) {
|
||||
spawnID = nodeID;
|
||||
} else if ( classname.starts_with("info_player_") ) {
|
||||
spawns.emplace_back(nodeID);
|
||||
// parse door
|
||||
if ( classname.starts_with("func_door") ) {
|
||||
auto& metadataDoor = metadata["door"];
|
||||
|
||||
metadataDoor["speed"] = metadata["speed"].as<float>(100.0f);
|
||||
metadataDoor["wait"] = metadata["wait"].as<float>(4.0f);
|
||||
metadataDoor["lip"] = metadata["lip"].as<float>(8.0f);
|
||||
metadataDoor["spawnflags"] = metadata["spawnflags"].as<int>(0);
|
||||
|
||||
if ( classname == "func_door" ) {
|
||||
auto movedirStr = metadata["movedir"].as<uf::stl::string>("");
|
||||
if ( movedirStr == "" && metadata["angle"].as<float>() ) {
|
||||
float ang = metadata["angle"].as<float>();
|
||||
if ( ang == -1 ) movedirStr = "-90 0 0";
|
||||
else if ( ang == -2 ) movedirStr = "90 0 0";
|
||||
else movedirStr = ::fmt::format("0 {} 0", ang);
|
||||
}
|
||||
|
||||
if ( movedirStr != "" ) {
|
||||
pod::Transform<> t;
|
||||
auto pyr = impl::str2vec<pod::Vector3f>( movedirStr ) * -DEG_2_RAD;
|
||||
t.orientation = uf::quaternion::euler( pyr );
|
||||
auto axes = uf::transform::axes( t );
|
||||
|
||||
metadataDoor["direction"] = uf::vector::encode( axes.forward );
|
||||
}
|
||||
} else if ( classname == "func_door_rotating" ) {
|
||||
metadataDoor["distance"] = metadata["distance"].as<float>(90.0f);
|
||||
|
||||
int flags = metadataDoor["spawnflags"].as<int>();
|
||||
pod::Vector3f axis = {0, 1, 0};
|
||||
if ( flags & 64 ) axis = {0, 0, 1};
|
||||
if ( flags & 128 ) axis = {1, 0, 0};
|
||||
|
||||
metadataDoor["axis"] = uf::vector::encode( axis );
|
||||
}
|
||||
}
|
||||
|
||||
// parse parent
|
||||
auto targetname = metadata["targetname"].as<uf::stl::string>("");
|
||||
if ( targetname != "" ) {
|
||||
targets[targetname] = nodeID;
|
||||
@ -471,6 +524,7 @@ namespace impl {
|
||||
// to-do: add additional parsing
|
||||
}
|
||||
|
||||
// re-parent entities
|
||||
uf::stl::vector<int32_t> newChildren;
|
||||
for ( auto nodeID : graph.root.children ) {
|
||||
auto& node = graph.nodes[nodeID];
|
||||
@ -488,19 +542,6 @@ namespace impl {
|
||||
}
|
||||
}
|
||||
graph.root.children = newChildren;
|
||||
|
||||
// no valid spawn
|
||||
UF_ASSERT( !(spawnID == -1 && spawns.empty()) ); // to-do: make the engine implicitly spawn the player at origin
|
||||
// pick a random candidate if none was found
|
||||
if ( spawnID == -1 ) spawnID = uf::stl::random( spawns );
|
||||
for ( auto nodeID : spawns ) {
|
||||
auto& node = graph.nodes[nodeID];
|
||||
if ( nodeID == spawnID ) {
|
||||
node.name = "info_player_start"; // mutate into spawn
|
||||
} else if ( node.name == "info_player_start" ) {
|
||||
node.name = ::fmt::format( "_{}", node.name ); // mutate out of spawn
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -559,26 +600,9 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
}
|
||||
}
|
||||
|
||||
size_t imageID = graph.images.size();
|
||||
auto imgKeyName = graph.images.emplace_back(matName);
|
||||
auto& image = storage.images[imgKeyName].data;
|
||||
|
||||
size_t textureID = graph.textures.size();
|
||||
auto texKeyName = graph.textures.emplace_back(matName);
|
||||
storage.textures[texKeyName].index = imageID;
|
||||
|
||||
size_t materialID = graph.materials.size();
|
||||
auto& material = impl::addMaterial( graph, matName );
|
||||
context.texdataToMaterial[texDataID] = materialID;
|
||||
|
||||
auto matKeyName = graph.materials.emplace_back(matName);
|
||||
auto& material = storage.materials[matKeyName];
|
||||
material.indexAlbedo = textureID;
|
||||
material.colorBase = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
material.factorMetallic = 0.0f;
|
||||
material.factorRoughness = 1.0f;
|
||||
material.factorOcclusion = 1.0f;
|
||||
|
||||
//UF_MSG_INFO("Material found: {}", matName);
|
||||
}
|
||||
|
||||
// read lightmaps
|
||||
@ -685,6 +709,7 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
{
|
||||
bool parsing = false;
|
||||
uf::stl::unordered_map<uf::stl::string, uf::stl::string> dict;
|
||||
uf::stl::vector<impl::BSP_IO> connections;
|
||||
|
||||
uf::stl::string string( (const char*) context.entities.data() );
|
||||
uf::stl::string line;
|
||||
@ -693,6 +718,7 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
if ( line.find("{") != uf::stl::string::npos ) {
|
||||
parsing = true;
|
||||
dict.clear();
|
||||
connections.clear();
|
||||
} else if ( line.find("}") != uf::stl::string::npos ) {
|
||||
parsing = false;
|
||||
|
||||
@ -700,13 +726,35 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
auto nodeID = graph.nodes.size();
|
||||
auto& node = graph.nodes.emplace_back();
|
||||
auto& metadata = node.metadata["valve"];
|
||||
for ( const auto& [k, v] : dict ) metadata[k] = v; // store as metadata for later parsing
|
||||
for ( const auto& [k, v] : dict ) metadata[k] = impl::processValue( v ); // store as metadata for later parsing
|
||||
|
||||
// insert IO
|
||||
if ( !connections.empty() ) {
|
||||
if ( ext::json::isNull( metadata["connections"] ) ) {
|
||||
ext::json::reserve( metadata["connections"], connections.size() );
|
||||
}
|
||||
for ( auto& io : connections ) {
|
||||
auto& entry = metadata["connections"].emplace_back();
|
||||
entry["output"] = io.output;
|
||||
entry["target"] = io.target;
|
||||
entry["input"] = io.input;
|
||||
entry["parameter"] = io.parameter;
|
||||
entry["delay"] = io.delay;
|
||||
entry["times"] = io.timesToFire;
|
||||
}
|
||||
}
|
||||
|
||||
// add node as child
|
||||
graph.root.children.emplace_back( nodeID );
|
||||
} else if ( parsing ) {
|
||||
uf::stl::string key, value;
|
||||
if ( impl::parseKeyValue(line, key, value) ) dict[key] = value;
|
||||
if ( impl::parseKeyValue(line, key, value) ) {
|
||||
if ( key.starts_with("On") ) {
|
||||
connections.emplace_back( impl::parseIO( key, value ) );
|
||||
continue;
|
||||
}
|
||||
dict[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -786,19 +834,19 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
material.factorRoughness = vmt["$roughness"].as<float>(1.0f);
|
||||
|
||||
if ( vmt["$envmap"].as<uf::stl::string>() != "" ) material.factorRoughness = 0.3f;
|
||||
if ( vmt["$phong"].as<uf::stl::string>("0") == "1" ) material.factorRoughness = std::min(material.factorRoughness, 0.5f);
|
||||
if ( vmt["$phong"].as<int>(0) == 1 ) material.factorRoughness = std::min(material.factorRoughness, 0.5f);
|
||||
|
||||
if ( vmt["$translucent"].as<uf::stl::string>("0") == "1" ) {
|
||||
if ( vmt["$translucent"].as<int>(0) == 1 ) {
|
||||
material.modeAlpha = 1; // BLEND
|
||||
} else if ( vmt["$alphatest"].as<uf::stl::string>("0") == "1" ) {
|
||||
} else if ( vmt["$alphatest"].as<int>(0) == 1 ) {
|
||||
material.modeAlpha = 2; // MASK
|
||||
material.factorAlphaCutoff = vmt["$alphatestreference"].as<float>(0.5f);
|
||||
}
|
||||
if ( vmt["$nocull"].as<uf::stl::string>("0") == "1" ) material.modeCull = 0;
|
||||
if ( vmt["$nocull"].as<int>(0) == 1 ) material.modeCull = 0;
|
||||
|
||||
// VMTs usually define emissive masks in the albedo's alpha channel or a separate mask
|
||||
// set it to a white glow for now until I can patch the shader
|
||||
if ( vmt["$selfillum"].as<uf::stl::string>("0") == "1" ) material.colorEmissive = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
if ( vmt["$selfillum"].as<int>(0) == 1 ) material.colorEmissive = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
if ( !vmt["$basetexture"].is<uf::stl::string>() ) goto PEETAH;
|
||||
|
||||
vtfPath = ::fmt::format("materials/{}.vtf", vmt["$basetexture"].as<uf::stl::string>());
|
||||
|
||||
@ -16,4 +16,39 @@ uf::stl::string impl::readString( std::ifstream& file ) {
|
||||
char c;
|
||||
while ( file.get(c) && c != '\0' ) str += c;
|
||||
return str;
|
||||
}
|
||||
|
||||
pod::Material& impl::addMaterial( pod::Graph& graph, const uf::stl::string& name ) {
|
||||
auto& storage = uf::graph::getStorage( graph );
|
||||
|
||||
size_t imageID = graph.images.size();
|
||||
auto imgKeyName = graph.images.emplace_back(name);
|
||||
auto& image = storage.images[imgKeyName].data;
|
||||
|
||||
size_t textureID = graph.textures.size();
|
||||
auto texKeyName = graph.textures.emplace_back(name);
|
||||
storage.textures[texKeyName].index = imageID;
|
||||
|
||||
auto matKeyName = graph.materials.emplace_back(name);
|
||||
auto& material = storage.materials[matKeyName];
|
||||
material.indexAlbedo = textureID;
|
||||
material.colorBase = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
material.factorMetallic = 0.0f;
|
||||
material.factorRoughness = 1.0f;
|
||||
material.factorOcclusion = 1.0f;
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
ext::json::Value impl::processValue( const uf::stl::string& v ) {
|
||||
if ( v.empty() ) return ext::json::Value(v);
|
||||
|
||||
char* end = nullptr;
|
||||
long intVal = std::strtol( v.c_str(), &end, 10 );
|
||||
if ( end == v.c_str() + v.size() ) return ext::json::Value( intVal );
|
||||
|
||||
float floatVal = std::strtof( v.c_str(), &end );
|
||||
if ( end == v.c_str() + v.size() ) return ext::json::Value( floatVal );
|
||||
|
||||
return ext::json::Value( v );
|
||||
}
|
||||
@ -285,23 +285,8 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
}
|
||||
|
||||
} else {
|
||||
// does not exist, register
|
||||
size_t imageID = graph.images.size();
|
||||
auto imgKeyName = graph.images.emplace_back(matName);
|
||||
auto& image = storage.images[imgKeyName].data;
|
||||
|
||||
size_t textureID = graph.textures.size();
|
||||
auto texKeyName = graph.textures.emplace_back(matName);
|
||||
storage.textures[texKeyName].index = imageID;
|
||||
|
||||
materialID = graph.materials.size();
|
||||
auto matKeyName = graph.materials.emplace_back(matName);
|
||||
auto& material = storage.materials[matKeyName];
|
||||
material.indexAlbedo = textureID;
|
||||
material.colorBase = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
material.factorMetallic = 0.0f;
|
||||
material.factorRoughness = 1.0f;
|
||||
material.factorOcclusion = 1.0f;
|
||||
auto& material = impl::addMaterial( graph, matName );
|
||||
}
|
||||
|
||||
meshlet.primitive.instance.materialID = materialID;
|
||||
|
||||
@ -127,7 +127,7 @@ pod::Mount ext::valve::createVpkMount( const uf::stl::string& uri, int priority
|
||||
auto ptr = userdata->get();
|
||||
if ( !ptr ) return 0;
|
||||
auto it = ptr->files.find( uf::string::lowercase( p ) );
|
||||
return it != ptr->files.end() ? it->second.metadata.entryLength : 0;
|
||||
return it != ptr->files.end() ? (it->second.metadata.preloadBytes + it->second.metadata.entryLength) : 0;
|
||||
};
|
||||
mount.mtime = [](const uf::stl::string&) -> size_t { return 0; },
|
||||
mount.read = [userdata](const uf::stl::string& p, uf::stl::vector<uint8_t>& buffer) {
|
||||
@ -300,6 +300,15 @@ bool ext::valve::readVpkRange( const pod::VpkArchive& vpk, const uf::stl::string
|
||||
if ( file ) {
|
||||
file.seekg(fileOffset + diskStart, std::ios::beg);
|
||||
file.read((char*)(buffer.data() + bufferOffset), len);
|
||||
|
||||
size_t actuallyRead = static_cast<size_t>(file.gcount());
|
||||
|
||||
if (actuallyRead < len) {
|
||||
buffer.resize(bufferOffset + actuallyRead);
|
||||
if (actuallyRead == 0 && buffer.empty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buffer.clear();
|
||||
UF_MSG_ERROR("Failed to open VPK chunk for ranged read: {}", archivePath);
|
||||
|
||||
@ -138,7 +138,7 @@ bool ext::valve::loadVmt( uf::Serializer& dict, const uf::stl::string& filename
|
||||
if ( key == "include" ) {
|
||||
ext::valve::loadVmt( dict, value );
|
||||
} else {
|
||||
dict[key] = value;
|
||||
dict[key] = impl::processValue( value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1949,11 +1949,11 @@ void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicD
|
||||
}
|
||||
#endif
|
||||
if ( !hasPipeline( descriptor ) ) {
|
||||
UF_MSG_DEBUG("{} has no valid pipeline: renderMode={}, pipeline={}", (void*) this, descriptor.renderMode, descriptor.pipeline);
|
||||
// UF_MSG_DEBUG("{} has no valid pipeline: renderMode={}, pipeline={}", (void*) this, descriptor.renderMode, descriptor.pipeline);
|
||||
return;
|
||||
}
|
||||
if ( !hasDescriptorSet( descriptor ) ) {
|
||||
UF_MSG_DEBUG("{} has no valid descriptor set: renderMode={}, pipeline={}", (void*) this, descriptor.renderMode, descriptor.pipeline);
|
||||
// UF_MSG_DEBUG("{} has no valid descriptor set: renderMode={}, pipeline={}", (void*) this, descriptor.renderMode, descriptor.pipeline);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -293,4 +293,8 @@ const uf::stl::string& uf::Audio::getFilename() const {
|
||||
}
|
||||
float uf::Audio::getDuration() const {
|
||||
return this->m_metadata ? this->m_metadata->info.duration : 0;
|
||||
}
|
||||
|
||||
bool uf::Audio::hasLoops() const {
|
||||
return this->m_metadata ? this->m_metadata->info.loop.has : false;
|
||||
}
|
||||
@ -382,7 +382,8 @@ void uf::debug::drawTexts( float dt ) {
|
||||
if ( rebuild ) uf::renderer::states::rebuild = true; // to-do: rebuild the defer mode only
|
||||
}
|
||||
}
|
||||
// think this has issues in OpenGL
|
||||
void uf::debug::draw( float dt ) {
|
||||
// uf::debug::drawLines( dt );
|
||||
// uf::debug::drawTexts( dt );
|
||||
uf::debug::drawLines( dt );
|
||||
uf::debug::drawTexts( dt );
|
||||
}
|
||||
@ -59,6 +59,10 @@ uf::stl::vector<uf::stl::string> uf::string::split( const uf::stl::string& str,
|
||||
if ( tokens.empty() ) tokens.emplace_back(str);
|
||||
return tokens;
|
||||
}
|
||||
uf::stl::vector<uf::stl::string> uf::string::split( const uf::stl::string& str, char delim ) {
|
||||
uf::stl::string d = ::fmt::format("{}", delim); // because it's such a pain apparently to convert a char to str
|
||||
return uf::string::split( str, d );
|
||||
}
|
||||
/*
|
||||
uf::stl::string uf::string::join( const uf::stl::vector<uf::stl::string>& strings, const uf::stl::string& delim, bool trailing ) {
|
||||
uf::stl::stringstream ss;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user