newdark: code cleanup (with uf::stl::reader), squashed bugs with ambience (to-do: fix attenuation), more I/O (buttons actually work, most things sans keycard readers emit the right sound, elevator lifts mostly work), added mask filtering for raycast to avoid hitting triggers, to-do: add a crosshair and draw a square around entity looking at, fix SIMD normalize being dreadfully slow
This commit is contained in:
parent
7bd018dae0
commit
988a61de7a
@ -1,3 +1,4 @@
|
||||
local ent = ent
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local darkMeta = metadata["dark"] or {}
|
||||
local soundMeta = darkMeta["sound"] or {}
|
||||
@ -7,10 +8,16 @@ local flags = soundMeta["flags"] or 0
|
||||
local baseVolume = tonumber(soundMeta["volume"]) or 1.0
|
||||
local radius = tonumber(soundMeta["radius"]) or 0.0
|
||||
|
||||
local startOn = (math.floor(flags / 1) % 2) ~= 0
|
||||
local environmental = (math.floor(flags / 2) % 2) ~= 0
|
||||
local environmental = (math.floor(flags / 1) % 2) ~= 0
|
||||
local isTurnedOff = (math.floor(flags / 4) % 2) ~= 0
|
||||
local isMusic = (math.floor(flags / 16) % 2) ~= 0
|
||||
|
||||
if isMusic then return end
|
||||
if schemaName == "" then return end
|
||||
|
||||
local isTurnedOff = (math.floor(flags / 4) % 2) ~= 0
|
||||
local startOn = not isTurnedOff
|
||||
|
||||
local isPlaying = false
|
||||
|
||||
local function playSound()
|
||||
@ -42,7 +49,7 @@ local function playSound()
|
||||
local schemaVol = tonumber(soundMeta["schema_volume"]) or 0
|
||||
local overrideVol = tonumber(soundMeta["volume"]) or 0
|
||||
|
||||
local baseVolume = 1.0
|
||||
local baseVolume = 0.2
|
||||
if overrideVol ~= 0 then baseVolume = math.pow(10, overrideVol / 2000.0) end
|
||||
local finalVolume = baseVolume * math.pow(10, schemaVol / 2000.0)
|
||||
|
||||
@ -57,6 +64,7 @@ local function playSound()
|
||||
|
||||
if radius > 0 and not environmental then
|
||||
payload.maxDistance = radius
|
||||
payload.referenceDistance = 1.0
|
||||
payload.rolloffFactor = 1.0
|
||||
end
|
||||
|
||||
@ -71,7 +79,7 @@ local function stopSound()
|
||||
})
|
||||
end
|
||||
|
||||
ent:addHook("dark:Message.%UID%", function(payload)
|
||||
ent:addHook("link:Message.%UID%", function(payload)
|
||||
local msg = payload.message
|
||||
|
||||
if msg == "TurnOn" then
|
||||
|
||||
@ -6,7 +6,7 @@ local physicsBody = ent:getComponent("PhysicsBody")
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local darkMeta = metadata["dark"] or {}
|
||||
local doorMeta = darkMeta["door"] or {}
|
||||
local schemaDb = doorMeta["schema_db"] or (darkMeta["schema_db"] or {})
|
||||
local schemaDb = darkMeta["schema_db"] or {}
|
||||
local classTags = doorMeta["class_tags"] or (darkMeta["class_tags"] or "")
|
||||
|
||||
local state = 0
|
||||
@ -192,7 +192,7 @@ ent:bind("tick", function(self)
|
||||
end
|
||||
end)
|
||||
|
||||
ent:addHook("dark:Message.%UID%", function(payload)
|
||||
ent:addHook("link:Message.%UID%", function(payload)
|
||||
local msg = payload.message
|
||||
|
||||
if msg == "TurnOn" then
|
||||
|
||||
107
bin/data/entities/scripts/dark/elevator.lua
Normal file
107
bin/data/entities/scripts/dark/elevator.lua
Normal file
@ -0,0 +1,107 @@
|
||||
local ent = ent
|
||||
local scene = entities.currentScene()
|
||||
|
||||
local transform = ent:getComponent("Transform")
|
||||
local physicsBody = ent:getComponent("PhysicsBody")
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local darkMeta = metadata["dark"] or {}
|
||||
|
||||
local speed = 2.0
|
||||
local state = 0
|
||||
local currentTargetPos = nil
|
||||
local activeWaypointDarkID = nil
|
||||
|
||||
local function findInitialWaypoint()
|
||||
local conns = darkMeta["connections"] or {}
|
||||
for _, conn in ipairs(conns) do
|
||||
if conn.flavor == "TPathInit" or conn.flavor == "TPathNext" or conn.flavor == "TPath" then
|
||||
return conn.target_id
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local currentWaypointID = findInitialWaypoint()
|
||||
|
||||
local function getWaypointPos(darkID)
|
||||
if not darkID then return nil end
|
||||
local targetUID = _G.DarkTargets[darkID]
|
||||
if targetUID then
|
||||
local wpEnt = entities.get(targetUID)
|
||||
if wpEnt then
|
||||
return wpEnt:getComponent("Transform").position, targetUID
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function advanceWaypoint(darkID)
|
||||
local targetUID = _G.DarkTargets[darkID]
|
||||
if targetUID then
|
||||
local wpEnt = entities.get(targetUID)
|
||||
if wpEnt then
|
||||
local wpMeta = wpEnt:getComponent("Metadata")["dark"] or {}
|
||||
local conns = wpMeta["connections"] or {}
|
||||
for _, conn in ipairs(conns) do
|
||||
if conn.flavor == "TPathNext" or conn.flavor == "TPath" then
|
||||
return conn.target_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
ent:bind("tick", function(self)
|
||||
if state == 1 and currentTargetPos then
|
||||
local myPos = transform.position
|
||||
local dir = currentTargetPos - myPos
|
||||
|
||||
local dist = dir:norm()
|
||||
local step = time.delta() * speed
|
||||
|
||||
if dist <= step then
|
||||
if physicsBody:initialized() then
|
||||
physicsBody:setVelocity(Vector3f(0, 0, 0))
|
||||
end
|
||||
transform.position = currentTargetPos
|
||||
state = 0
|
||||
|
||||
local nextWp = advanceWaypoint(activeWaypointDarkID)
|
||||
if nextWp then
|
||||
currentWaypointID = nextWp
|
||||
end
|
||||
|
||||
else
|
||||
local vel = dir:normalize() * speed
|
||||
if physicsBody:initialized() then
|
||||
physicsBody:setVelocity(vel)
|
||||
else
|
||||
transform.position = myPos + (vel * time.delta())
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
ent:addHook("link:Message.%UID%", function(payload)
|
||||
local msg = payload.message
|
||||
|
||||
|
||||
if (msg == "TurnOn" or msg == "TurnOff") and state == 0 then
|
||||
local targetPos, tUid = getWaypointPos(currentWaypointID)
|
||||
|
||||
if targetPos and (transform.position - targetPos):norm() < 0.1 then
|
||||
local nextWp = advanceWaypoint(currentWaypointID)
|
||||
if nextWp then
|
||||
currentWaypointID = nextWp
|
||||
targetPos, tUid = getWaypointPos(currentWaypointID)
|
||||
end
|
||||
end
|
||||
|
||||
if targetPos then
|
||||
currentTargetPos = targetPos
|
||||
activeWaypointDarkID = currentWaypointID
|
||||
state = 1
|
||||
end
|
||||
end
|
||||
end)
|
||||
@ -1,33 +1,165 @@
|
||||
local ent = ent
|
||||
local scene = entities.currentScene()
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
|
||||
local FLAVOR_CONTROL_DEVICE = 1
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local darkMeta = metadata["dark"] or {}
|
||||
local schemaDb = darkMeta["schema_db"] or {}
|
||||
local classTags = darkMeta["class_tags"] or ""
|
||||
|
||||
_G.DarkTargets = _G.DarkTargets or {}
|
||||
local darkMeta = metadata["dark"] or {}
|
||||
if darkMeta["id"] then
|
||||
_G.DarkTargets[darkMeta["id"]] = ent:uid()
|
||||
end
|
||||
|
||||
ent:addHook("dark:Message.%UID%", function(payload)
|
||||
_G.DarkScripts = _G.DarkScripts or {}
|
||||
|
||||
_G.DarkScripts["RelayTrap"] = function(entity, payload, dMeta)
|
||||
local msg = payload.message
|
||||
local eName = entity or entity:uid()
|
||||
--print(string.format("%s (RelayTrap) is forwarding %s down its links...", eName, msg))
|
||||
|
||||
entity:callHook("link:Broadcast.%UID%", {
|
||||
message = msg,
|
||||
flavors = { "ControlDevice", "SwitchLink" },
|
||||
caller = entity:uid(),
|
||||
callerDarkID = payload.callerDarkID
|
||||
})
|
||||
end
|
||||
_G.DarkScripts["ElevatorButton"] = _G.DarkScripts["RelayTrap"]
|
||||
|
||||
_G.DarkScripts["RequireAllTrap"] = function(entity, payload, dMeta)
|
||||
local msg = payload.message
|
||||
local callerDarkID = payload.callerDarkID
|
||||
|
||||
_G.RAT_States = _G.RAT_States or {}
|
||||
local uid = entity:uid()
|
||||
_G.RAT_States[uid] = _G.RAT_States[uid] or { inputs = {}, wasOn = false }
|
||||
local state = _G.RAT_States[uid]
|
||||
|
||||
if callerDarkID then
|
||||
state.inputs[callerDarkID] = (msg == "TurnOn")
|
||||
end
|
||||
|
||||
local allOn = true
|
||||
local incoming = dMeta["incoming_connections"] or {}
|
||||
for i = 1, #incoming do
|
||||
local conn = incoming[i]
|
||||
if conn.flavor == "ControlDevice" or conn.flavor == "SwitchLink" then
|
||||
if not state.inputs[conn.source_id] then
|
||||
allOn = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if allOn and not state.wasOn then
|
||||
state.wasOn = true
|
||||
--print(entity .. " (RequireAllTrap) conditions met! Broadcasting TurnOn.")
|
||||
entity:callHook("link:Broadcast.%UID%", { message = "TurnOn", flavors = { "ControlDevice", "SwitchLink" }, callerDarkID = dMeta["id"], caller = uid })
|
||||
elseif not allOn and state.wasOn then
|
||||
state.wasOn = false
|
||||
--print(entity .. " (RequireAllTrap) condition lost! Broadcasting TurnOff.")
|
||||
entity:callHook("link:Broadcast.%UID%", { message = "TurnOff", flavors = { "ControlDevice", "SwitchLink" }, callerDarkID = dMeta["id"], caller = uid })
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
_G.DarkScripts["BaseElevator"] = function(entity, payload, dMeta)
|
||||
local msg = payload.message
|
||||
local eName = entity or entity:uid()
|
||||
|
||||
print(string.format("%s (Elevator) received command: %s", eName, msg))
|
||||
|
||||
if msg == "TurnOn" or msg == "TurnOff" then
|
||||
print(string.format("--> Commanding Lift '%s' to move!", eName))
|
||||
end
|
||||
end
|
||||
|
||||
_G.DarkScripts["Elevator"] = _G.DarkScripts["BaseElevator"]
|
||||
]]
|
||||
|
||||
ent:addHook("link:Message.%UID%", function(payload)
|
||||
local msg = payload.message
|
||||
local caller = payload.caller
|
||||
local eName = ent:name() or ent:uid()
|
||||
|
||||
if msg == "TurnOn" then
|
||||
print("Dark I/O [".. ent:name() .. "]: Received TurnOn from " .. tostring(caller))
|
||||
elseif msg == "TurnOff" then
|
||||
print("Dark I/O [".. ent:name() .. "]: Received TurnOff from " .. tostring(caller))
|
||||
--print(string.format("Entity %s (Dark ID: %s) received message: %s from UID: %s", eName, darkMeta["id"] or "Unknown", msg, tostring(caller)))
|
||||
|
||||
local scripts = darkMeta["scripts"] or {}
|
||||
local scriptHandled = false
|
||||
|
||||
for _, script in ipairs(scripts) do
|
||||
if _G.DarkScripts[script] then
|
||||
_G.DarkScripts[script](ent, payload, darkMeta)
|
||||
scriptHandled = true
|
||||
end
|
||||
end
|
||||
|
||||
if not scriptHandled then
|
||||
if eName:lower():match("filter") or eName:lower():match("trigger") then
|
||||
_G.DarkScripts["RelayTrap"](ent, payload, darkMeta)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local connections = metadata["connections"]
|
||||
if not connections then return end
|
||||
local function playButtonSound()
|
||||
if not classTags or classTags == "" then return end
|
||||
local myDeviceType = string.match(classTags, "DeviceType%s+([^%s,]+)")
|
||||
|
||||
ent:addHook("dark:Broadcast.%UID%", function(payload)
|
||||
local bestMatch = nil
|
||||
local bestScore = -1
|
||||
|
||||
for _, schema in ipairs(schemaDb) do
|
||||
local sTags = schema.tags or ""
|
||||
local score = 0
|
||||
|
||||
if myDeviceType then
|
||||
local paddedTags = sTags .. ","
|
||||
|
||||
if string.find(paddedTags, "DeviceType%s+" .. myDeviceType .. "[,%s]") then
|
||||
score = score + 10
|
||||
elseif string.find(paddedTags, "DeviceType%s+Gen[,%s]") then
|
||||
score = score + 5
|
||||
elseif string.find(sTags, "DeviceType") then
|
||||
score = score - 10
|
||||
end
|
||||
|
||||
if score > 0 then
|
||||
if string.find(sTags, "Reject") then
|
||||
score = score + 5
|
||||
elseif string.find(sTags, "StateChange") or string.find(sTags, "Activate") then
|
||||
score = score + 5
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if score > bestScore then
|
||||
bestScore = score
|
||||
bestMatch = schema
|
||||
end
|
||||
end
|
||||
|
||||
print("Button sound resolution:", ent, classTags, myDeviceType, bestMatch and bestMatch.name or "None", bestScore)
|
||||
|
||||
if bestMatch and bestScore > 0 and bestMatch.wavs and #bestMatch.wavs > 0 then
|
||||
local pick = bestMatch.wavs[math.random(#bestMatch.wavs)]
|
||||
local resolvedUrl = string.resolveURI(pick, metadata["system"]["root"])
|
||||
ent:callHook("sound:Emit.%UID%", {
|
||||
filename = resolvedUrl, spatial = true, volume = 1.0, maxDistance = 15.0, rolloffFactor = 1.0, unique = true
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local connections = darkMeta["connections"]
|
||||
|
||||
ent:addHook("link:Broadcast.%UID%", function(payload)
|
||||
local msg = payload.message
|
||||
local validFlavors = payload.flavors or { "ControlDevice", "SwitchLink" }
|
||||
|
||||
if not connections then
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, #connections do
|
||||
local conn = connections[i]
|
||||
local isValid = false
|
||||
@ -37,12 +169,15 @@ ent:addHook("dark:Broadcast.%UID%", function(payload)
|
||||
|
||||
if isValid then
|
||||
local targetUID = _G.DarkTargets[conn.target_id]
|
||||
--print(string.format("Broadcasting %s to Dark ID %s (UID: %s) via %s", msg, conn.target_id, tostring(targetUID), conn.flavor))
|
||||
|
||||
if targetUID then
|
||||
local targetEnt = entities.get(targetUID)
|
||||
if targetEnt and targetEnt:uid() then
|
||||
targetEnt:callHook("dark:Message." .. targetUID, {
|
||||
targetEnt:callHook("link:Message." .. targetUID, {
|
||||
message = msg,
|
||||
caller = ent:uid()
|
||||
caller = ent:uid(),
|
||||
callerDarkID = darkMeta["id"] or payload.callerDarkID
|
||||
})
|
||||
end
|
||||
end
|
||||
@ -50,13 +185,39 @@ ent:addHook("dark:Broadcast.%UID%", function(payload)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
if darkMeta["frob"] ~= nil then
|
||||
ent:addHook("entity:Use.%UID%", function(payload)
|
||||
local frobWorld = darkMeta["frob"]["world"] or 0
|
||||
|
||||
local kFrobMove = 1 -- 1 << 0
|
||||
local kFrobScript = 2 -- 1 << 1
|
||||
|
||||
if bit.band(frobWorld, kFrobScript) ~= 0 then
|
||||
print(ent, json.encode( darkMeta ))
|
||||
playButtonSound()
|
||||
|
||||
ent:callHook("link:Broadcast.%UID%", {
|
||||
message = "TurnOn",
|
||||
flavors = { "ControlDevice", "SwitchLink" },
|
||||
caller = payload.user,
|
||||
callerDarkID = darkMeta["id"]
|
||||
})
|
||||
end
|
||||
|
||||
if bit.band(frobWorld, kFrobMove) ~= 0 then
|
||||
print("Item picked up!")
|
||||
-- ent:callHook("inventory:Pickup.%UID%", { user = payload.user })
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
ent:addHook("entity:Destroy.%UID%", function()
|
||||
if darkMeta["id"] and _G.DarkTargets[darkMeta["id"]] == ent:uid() then
|
||||
_G.DarkTargets[darkMeta["id"]] = nil
|
||||
end
|
||||
end)
|
||||
|
||||
ent:addHook("entity:Use.%UID%", function(payload)
|
||||
if payload.user == ent:uid() then return end
|
||||
ent:callHook("dark:Broadcast.%UID%", { message = "TurnOn" })
|
||||
if _G.RAT_States and _G.RAT_States[ent:uid()] then
|
||||
_G.RAT_States[ent:uid()] = nil
|
||||
end
|
||||
end)
|
||||
@ -1,3 +1,4 @@
|
||||
local ent = ent
|
||||
local scene = entities.currentScene()
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local activeSongId = metadata["dark"]["mission song"] or "song02"
|
||||
@ -13,12 +14,9 @@ local currentLoopCount = 0
|
||||
local initializedPlayer = false
|
||||
|
||||
if not songData then
|
||||
print("[Music Manager] ERROR: Active song '" .. tostring(activeSongId) .. "' not found!")
|
||||
return
|
||||
end
|
||||
|
||||
print("[Music Manager] Bound Mission Song: " .. activeSongId)
|
||||
|
||||
for _, markerData in ipairs(bakedMarkers) do
|
||||
table.insert(musicMarkers, {
|
||||
position = Vector3f(markerData.position[1], markerData.position[2], markerData.position[3]),
|
||||
@ -27,7 +25,6 @@ for _, markerData in ipairs(bakedMarkers) do
|
||||
})
|
||||
end
|
||||
|
||||
print("[Music Manager] Pre-caching song assets...")
|
||||
for _, section in ipairs(songData.sections or {}) do
|
||||
if section.samples then
|
||||
for _, sampleName in ipairs(section.samples) do
|
||||
@ -83,19 +80,15 @@ local function playSection(sectionIdx, isQueue)
|
||||
local url = getSampleUrl(section)
|
||||
if url then
|
||||
if isQueue then
|
||||
print(string.format("[Music Manager] Queuing section: %s", section.id))
|
||||
ent:callHook("sound:QueueTrack.%UID%", { filename = url, layer = 1 })
|
||||
else
|
||||
print(string.format("[Music Manager] Forcing immediate section: %s", section.id))
|
||||
ent:callHook("sound:PlayTrack.%UID%", { filename = url, layer = 1, loop = false })
|
||||
|
||||
-- Immediately predict and queue the next track to prevent gaps!
|
||||
local nextIdx, nextLoop = predictNextSection(currentSectionIdx, currentLoopCount)
|
||||
if nextIdx then
|
||||
local nextSec = songData.sections[nextIdx + 1]
|
||||
local nextUrl = getSampleUrl(nextSec)
|
||||
if nextUrl then
|
||||
print(string.format("[Music Manager] Pre-queuing next section: %s", nextSec.id))
|
||||
ent:callHook("sound:QueueTrack.%UID%", { filename = nextUrl, layer = 1 })
|
||||
end
|
||||
end
|
||||
@ -141,16 +134,12 @@ local function processEvent(eventName)
|
||||
end
|
||||
end
|
||||
|
||||
print(string.format("[Music Manager] Event '%s' triggered transition to section %d", eventFound.event, targetSection))
|
||||
|
||||
if eventName ~= "" then
|
||||
currentLoopCount = 0
|
||||
playSection(targetSection, false)
|
||||
else
|
||||
playSection(targetSection, true)
|
||||
end
|
||||
else
|
||||
print(string.format("[Music Manager] WARNING: Event '%s' ignored (not valid from section %d)", eventName, currentSectionIdx))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,26 +1,56 @@
|
||||
local ent = ent
|
||||
local scene = entities.currentScene()
|
||||
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local darkMeta = metadata["dark"] or {}
|
||||
local soundMeta = darkMeta["sound"] or {}
|
||||
local schemaDb = darkMeta["schema_db"] or {}
|
||||
local connections = darkMeta["connections"] or {}
|
||||
|
||||
ent:addHook("dark:Message.%UID%", function(payload)
|
||||
local isPlaying = false
|
||||
|
||||
local nativeSchema = soundMeta["schema"] or ""
|
||||
|
||||
ent:addHook("link:Message.%UID%", function(payload)
|
||||
local msg = payload.message
|
||||
|
||||
if msg == "TurnOn" then
|
||||
if msg == "TurnOn" and not isPlaying then
|
||||
local urlToPlay = ""
|
||||
|
||||
for _, conn in ipairs(connections) do
|
||||
if conn.flavor == "SoundDescription" and conn.wavs and #conn.wavs > 0 then
|
||||
local pick = conn.wavs[math.random(#conn.wavs)]
|
||||
local resolvedUrl = string.resolveURI(pick, metadata["system"]["root"])
|
||||
|
||||
ent:callHook("sound:Emit.%UID%", {
|
||||
filename = resolvedUrl,
|
||||
spatial = true,
|
||||
streamed = false,
|
||||
volume = 1.0,
|
||||
unique = true,
|
||||
loop = false
|
||||
})
|
||||
urlToPlay = conn.wavs[math.random(#conn.wavs)]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if urlToPlay == "" and nativeSchema ~= "" then
|
||||
for _, entry in ipairs(schemaDb) do
|
||||
if entry.name:lower() == nativeSchema:lower() then
|
||||
if entry.wavs and #entry.wavs > 0 then
|
||||
urlToPlay = entry.wavs[math.random(#entry.wavs)]
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if urlToPlay ~= "" then
|
||||
local resolvedUrl = string.resolveURI(urlToPlay, metadata["system"]["root"])
|
||||
isPlaying = true
|
||||
|
||||
ent:callHook("sound:Emit.%UID%", {
|
||||
filename = resolvedUrl,
|
||||
spatial = false,
|
||||
streamed = true,
|
||||
volume = 1.0,
|
||||
unique = true,
|
||||
loop = false
|
||||
})
|
||||
end
|
||||
|
||||
elseif msg == "TurnOff" and isPlaying then
|
||||
isPlaying = false
|
||||
ent:callHook("sound:Stop.%UID%", {})
|
||||
end
|
||||
end)
|
||||
@ -24,7 +24,7 @@ ent:bind( "tick", function(self)
|
||||
|
||||
if not touching[uid] then
|
||||
touching[uid] = true
|
||||
ent:queueHook("dark:Broadcast.%UID%", { message = "TurnOn" }, 0)
|
||||
ent:queueHook("link:Broadcast.%UID%", { message = "TurnOn" }, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -32,7 +32,7 @@ ent:bind( "tick", function(self)
|
||||
for uid, _ in pairs(touching) do
|
||||
if not currentCollisions[uid] then
|
||||
touching[uid] = nil
|
||||
ent:queueHook("dark:Broadcast.%UID%", { message = "TurnOff" }, 0)
|
||||
ent:queueHook("link:Broadcast.%UID%", { message = "TurnOff" }, 0)
|
||||
end
|
||||
end
|
||||
end )
|
||||
@ -236,6 +236,8 @@ end
|
||||
ent:addHook( "entity:Use.%UID%", function( payload )
|
||||
if payload.user ~= ent:uid() then return end
|
||||
|
||||
print( "Use:", entities.get( payload.uid ) )
|
||||
|
||||
local validUse = false
|
||||
if heldObject.uid == 0 and payload.depth > 0 then
|
||||
local prop = entities.get( payload.uid )
|
||||
|
||||
@ -8,30 +8,6 @@ namespace impl {
|
||||
const float darkToMeters = 0.7f;
|
||||
typedef uf::Meshlet_T<uf::graph::mesh::Skinned, uint32_t> Meshlet;
|
||||
|
||||
// could be dedicated functions in the engine
|
||||
template<typename T>
|
||||
inline bool readStruct(const uf::stl::vector<uint8_t>& buffer, uint32_t& offset, T& outValue, size_t readSize = sizeof(T)) {
|
||||
if (offset + readSize > buffer.size()) return false;
|
||||
|
||||
if ( readSize < sizeof(T) ) std::memset(&outValue, 0, sizeof(T));
|
||||
|
||||
size_t copySize = std::min(sizeof(T), readSize);
|
||||
std::memcpy(&outValue, buffer.data() + offset, copySize);
|
||||
|
||||
offset += readSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool readArray(const uf::stl::vector<uint8_t>& buffer, uint32_t& offset, size_t count, uf::stl::vector<T>& outArray) {
|
||||
size_t bytes = count * sizeof(T);
|
||||
if (offset + bytes > buffer.size()) return false;
|
||||
outArray.resize(count);
|
||||
std::memcpy(outArray.data(), buffer.data() + offset, bytes);
|
||||
offset += bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline float findWrap( float x ) {
|
||||
return -64.0f * std::floor(x / 64.0f);
|
||||
};
|
||||
|
||||
@ -70,6 +70,8 @@ namespace uf {
|
||||
void UF_API rolloff( pod::AudioSource& source, float v );
|
||||
float UF_API maxDistance( const pod::AudioSource& source );
|
||||
void UF_API maxDistance( pod::AudioSource& source, float v );
|
||||
float UF_API referenceDistance( const pod::AudioSource& source );
|
||||
void UF_API referenceDistance( pod::AudioSource& source, float v );
|
||||
|
||||
float UF_API distance( const pod::Vector3f& position );
|
||||
float UF_API occlusion( const pod::Vector3f& position );
|
||||
|
||||
@ -19,9 +19,9 @@ namespace impl {
|
||||
|
||||
namespace uf {
|
||||
namespace physics {
|
||||
pod::RayQuery UF_API rayCast( const pod::Ray&, const pod::PhysicsBody&, float = FLT_MAX );
|
||||
pod::RayQuery UF_API rayCast( const pod::Ray&, const pod::World&, float = FLT_MAX );
|
||||
pod::RayQuery UF_API rayCast( const pod::Ray&, const pod::World&, const pod::PhysicsBody*, float = FLT_MAX );
|
||||
pod::RayQuery UF_API rayCast( const pod::Ray&, const pod::PhysicsBody&, float = FLT_MAX, uint32_t mask = pod::Collider::MASK_PHYSICAL );
|
||||
pod::RayQuery UF_API rayCast( const pod::Ray&, const pod::World&, float = FLT_MAX, uint32_t mask = pod::Collider::MASK_PHYSICAL );
|
||||
pod::RayQuery UF_API rayCast( const pod::Ray&, const pod::World&, const pod::PhysicsBody*, float = FLT_MAX, uint32_t mask = pod::Collider::MASK_PHYSICAL );
|
||||
|
||||
float UF_API occlusion( const pod::Vector3f& to, const pod::Vector3f& from );
|
||||
uf::stl::string UF_API getRayMaterialName( const pod::RayQuery& query );
|
||||
|
||||
@ -318,24 +318,27 @@ namespace pod {
|
||||
CATEGORY_NONE = 0,
|
||||
CATEGORY_STATIC = 1 << 0,
|
||||
CATEGORY_DYNAMIC = 1 << 1,
|
||||
CATEGORY_PLAYER = 1 << 2,
|
||||
CATEGORY_PLAYER = 1 << 2,
|
||||
CATEGORY_NPC = 1 << 3,
|
||||
CATEGORY_TRIGGER = 1 << 4,
|
||||
CATEGORY_PROJECTILE = 1 << 5,
|
||||
CATEGORY_INTERACTABLE = 1 << 6,
|
||||
CATEGORY_CHARACTER = CATEGORY_PLAYER | CATEGORY_NPC,
|
||||
CATEGORY_ALL = 0xFFFFFFFF
|
||||
};
|
||||
// what it collides with
|
||||
enum CollisionMask : uint32_t {
|
||||
MASK_NONE = 0,
|
||||
MASK_NONE = 0,
|
||||
MASK_STATIC = CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC | CATEGORY_PROJECTILE,
|
||||
MASK_DYNAMIC = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC | CATEGORY_TRIGGER,
|
||||
MASK_PLAYER = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_NPC | CATEGORY_PROJECTILE | CATEGORY_TRIGGER,
|
||||
MASK_DYNAMIC = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC | CATEGORY_TRIGGER | CATEGORY_INTERACTABLE,
|
||||
MASK_PLAYER = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_NPC | CATEGORY_PROJECTILE | CATEGORY_TRIGGER | CATEGORY_INTERACTABLE,
|
||||
MASK_NPC = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_PROJECTILE | CATEGORY_TRIGGER,
|
||||
MASK_TRIGGER = CATEGORY_PLAYER | CATEGORY_NPC,
|
||||
MASK_PROJECTILE = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC,
|
||||
MASK_CHARACTER = MASK_PLAYER | MASK_NPC,
|
||||
MASK_ALL = 0xFFFFFFFF
|
||||
MASK_ALL = 0xFFFFFFFF,
|
||||
|
||||
MASK_PHYSICAL = MASK_ALL & ~CATEGORY_TRIGGER,
|
||||
};
|
||||
|
||||
pod::ShapeType type;
|
||||
|
||||
@ -104,6 +104,7 @@ namespace uf {
|
||||
template<typename T, size_t N> /*FORCE_INLINE*/ pod::Vector<T, N> /*UF_API*/ copy( const pod::Vector<T, N>& = {}); // creates a copy of a vector (for whatever reason)
|
||||
template<typename T, size_t N> /*FORCE_INLINE*/ pod::Vector<T, N> /*UF_API*/ copy( const T* ); // creates a copy of a vector (for whatever reason)
|
||||
template<typename T, size_t N, typename U> /*FORCE_INLINE*/ pod::Vector<T, N> /*UF_API*/ cast( const U& from ); // casts one vector of one type to another (of the same size)
|
||||
template<typename T, size_t N> /*FORCE_INLINE*/ pod::Vector<T, N> /*UF_API*/ fill( const T ); //
|
||||
// Equality checking
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ equals( const T& left, const T& right ); // equality check between two vectors (==)
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ notEquals( const T& left, const T& right ); // equality check between two vectors (==)
|
||||
@ -112,6 +113,14 @@ namespace uf {
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ greater( const T& left, const T& right ); // equality check between two vectors (>)
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ greaterEquals( const T& left, const T& right ); // equality check between two vectors (>=)
|
||||
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ equals( const T& left, const typename T::type_t right ); // equality check between a vector's components (==)
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ notEquals( const T& left, const typename T::type_t right ); // equality check between a vector's components (==)
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ less( const T& left, const typename T::type_t right ); // equality check between a vector's components (<)
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ lessEquals( const T& left, const typename T::type_t right ); // equality check between a vector's components (<=)
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ greater( const T& left, const typename T::type_t right ); // equality check between a vector's components (>)
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ greaterEquals( const T& left, const typename T::type_t right ); // equality check between a vector's components (>=)
|
||||
|
||||
|
||||
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ isValid( const T& v ); // checks if all components are valid (non NaN, inf, etc.)
|
||||
// Basic arithmetic
|
||||
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ add( const T& left, const T& right ); // adds two vectors of same type and size together
|
||||
@ -220,6 +229,12 @@ namespace uf {
|
||||
FORCE_INLINE bool operator<=(const Vector<T,N>& rhs) const { return uf::vector::lessEquals(*this, rhs); } \
|
||||
FORCE_INLINE bool operator>(const Vector<T,N>& rhs) const { return uf::vector::greater(*this, rhs); } \
|
||||
FORCE_INLINE bool operator>=(const Vector<T,N>& rhs) const { return uf::vector::greaterEquals(*this, rhs); } \
|
||||
FORCE_INLINE bool operator==(const T rhs) const { return uf::vector::equals(*this, rhs); } \
|
||||
FORCE_INLINE bool operator!=(const T rhs) const { return uf::vector::notEquals(*this, rhs); } \
|
||||
FORCE_INLINE bool operator<(const T rhs) const { return uf::vector::less(*this, rhs); } \
|
||||
FORCE_INLINE bool operator<=(const T rhs) const { return uf::vector::lessEquals(*this, rhs); } \
|
||||
FORCE_INLINE bool operator>(const T rhs) const { return uf::vector::greater(*this, rhs); } \
|
||||
FORCE_INLINE bool operator>=(const T rhs) const { return uf::vector::greaterEquals(*this, rhs); } \
|
||||
FORCE_INLINE explicit operator bool() const { return uf::vector::notEquals(*this, Vector<T,N>{}); } \
|
||||
template<typename U, size_t M> FORCE_INLINE operator Vector<U,M>() const { return uf::vector::cast<U,M>(*this); }
|
||||
|
||||
|
||||
@ -56,6 +56,14 @@ pod::Vector<T, N> uf::vector::cast( const U& from ) {
|
||||
to[i] = from[i];
|
||||
return to;
|
||||
}
|
||||
template<typename T, size_t N>
|
||||
pod::Vector<T, N> uf::vector::fill( const T v ) {
|
||||
pod::Vector<T, N> res;
|
||||
FOR_EACH(T::size, {
|
||||
res[i] = v;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
template<typename T>
|
||||
bool uf::vector::equals( const T& left, const T& right ) {
|
||||
#if UF_USE_SIMD
|
||||
@ -134,6 +142,86 @@ bool uf::vector::greaterEquals( const T& left, const T& right ) {
|
||||
});
|
||||
return result;
|
||||
}
|
||||
//
|
||||
template<typename T>
|
||||
bool uf::vector::equals( const T& left, const typename T::type_t right ) {
|
||||
#if UF_USE_SIMD
|
||||
if constexpr ( simd_able_v<typename T::type_t> && T::size == 4 ) {
|
||||
return uf::simd::all( uf::simd::equals( left, right ) );
|
||||
}
|
||||
#endif
|
||||
bool result = true;
|
||||
FOR_EACH(T::size, {
|
||||
if ( !(left[i] == right) ) result = false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
template<typename T>
|
||||
bool uf::vector::notEquals( const T& left, const typename T::type_t right ) {
|
||||
#if UF_USE_SIMD
|
||||
if constexpr ( simd_able_v<typename T::type_t> && T::size == 4 ) {
|
||||
return uf::simd::all( uf::simd::notEquals( left, right ) );
|
||||
}
|
||||
#endif
|
||||
bool result = true;
|
||||
FOR_EACH(T::size, {
|
||||
if ( !(left[i] != right) ) result = false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
template<typename T>
|
||||
bool uf::vector::less( const T& left, const typename T::type_t right ) {
|
||||
#if UF_USE_SIMD
|
||||
if constexpr ( simd_able_v<typename T::type_t> && T::size == 4 ) {
|
||||
return uf::simd::all( uf::simd::less( left, right ) );
|
||||
}
|
||||
#endif
|
||||
bool result = true;
|
||||
FOR_EACH(T::size, {
|
||||
if ( !(left[i] < right) ) result = false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
template<typename T>
|
||||
bool uf::vector::lessEquals( const T& left, const typename T::type_t right ) {
|
||||
#if UF_USE_SIMD
|
||||
if constexpr ( simd_able_v<typename T::type_t> && T::size == 4 ) {
|
||||
return uf::simd::all( uf::simd::lessEquals( left, right ) );
|
||||
}
|
||||
#endif
|
||||
bool result = true;
|
||||
FOR_EACH(T::size, {
|
||||
if ( !(left[i] <= right) ) result = false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
template<typename T>
|
||||
bool uf::vector::greater( const T& left, const typename T::type_t right ) {
|
||||
#if UF_USE_SIMD
|
||||
if constexpr ( simd_able_v<typename T::type_t> && T::size == 4 ) {
|
||||
return uf::simd::all( uf::simd::greater( left, right ) );
|
||||
}
|
||||
#endif
|
||||
bool result = true;
|
||||
FOR_EACH(T::size, {
|
||||
if ( !(left[i] > right) ) result = false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
template<typename T>
|
||||
bool uf::vector::greaterEquals( const T& left, const typename T::type_t right ) {
|
||||
#if UF_USE_SIMD
|
||||
if constexpr ( simd_able_v<typename T::type_t> && T::size == 4 ) {
|
||||
return uf::simd::all( uf::simd::greaterEquals( left, right ) );
|
||||
}
|
||||
#endif
|
||||
bool result = true;
|
||||
FOR_EACH(T::size, {
|
||||
if ( !(left[i] >= right) ) result = false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
//
|
||||
template<typename T>
|
||||
bool uf::vector::isValid( const T& v ) {
|
||||
return uf::vector::equals( v, v );
|
||||
@ -274,7 +362,7 @@ T uf::vector::divide( const T& vector, typename T::type_t scalar ) {
|
||||
}
|
||||
#endif
|
||||
T res;
|
||||
float recip = static_cast<typename T::type_t>(1) / scalar;
|
||||
float recip = 1.0f / static_cast<float>(scalar);
|
||||
FOR_EACH(T::size, {
|
||||
res[i] = vector[i] * recip;
|
||||
});
|
||||
@ -505,10 +593,10 @@ T uf::vector::min( const T& left, const T& right ) {
|
||||
return res;
|
||||
}
|
||||
template<typename T>
|
||||
T uf::vector::min( const T& vector, typename T::type_t min ) {
|
||||
T uf::vector::min( const T& vector, typename T::type_t right ) {
|
||||
T res = vector;
|
||||
FOR_EACH(T::size, {
|
||||
res[i] = std::min( res[i], min );
|
||||
res[i] = std::min( res[i], right );
|
||||
});
|
||||
return res;
|
||||
}
|
||||
@ -534,10 +622,10 @@ T uf::vector::max( const T& left, const T& right ) {
|
||||
return res;
|
||||
}
|
||||
template<typename T>
|
||||
T uf::vector::max( const T& vector, typename T::type_t max ) {
|
||||
T uf::vector::max( const T& vector, typename T::type_t right ) {
|
||||
T res = vector;
|
||||
FOR_EACH(T::size, {
|
||||
res[i] = std::max( res[i], max );
|
||||
res[i] = std::max( res[i], right );
|
||||
});
|
||||
return res;
|
||||
}
|
||||
@ -728,8 +816,8 @@ typename T::type_t uf::vector::norm( const T& vector ) {
|
||||
}
|
||||
template<typename T>
|
||||
T uf::vector::normalize( const T& vector ) {
|
||||
#if UF_USE_SIMD
|
||||
if constexpr ( std::is_same_v<T,float> ) {
|
||||
#if 0 && UF_USE_SIMD // causes massive frame drops
|
||||
if constexpr ( std::is_same_v<typename T::type_t, float> ) {
|
||||
return uf::simd::normalize( vector );
|
||||
}
|
||||
#endif
|
||||
@ -745,13 +833,12 @@ T uf::vector::normalize( const T& vector ) {
|
||||
|
||||
template<typename T>
|
||||
T uf::vector::clampMagnitude( const T& v, float maxMag ) {
|
||||
T res = v;
|
||||
float mag = uf::vector::magnitude( res );
|
||||
if ( mag > maxMag ) {
|
||||
res /= (maxMag / sqrt(mag));
|
||||
}
|
||||
|
||||
return res;
|
||||
T res = v;
|
||||
float magSq = uf::vector::magnitude( res );
|
||||
if ( magSq > (maxMag * maxMag) ) {
|
||||
res *= (maxMag / sqrt(magSq));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
81
engine/inc/uf/utils/memory/reader.h
Normal file
81
engine/inc/uf/utils/memory/reader.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include "vector.h"
|
||||
|
||||
namespace uf {
|
||||
namespace stl {
|
||||
// technically NOT a templated class, but it works with templates
|
||||
class UF_API reader {
|
||||
private:
|
||||
const uf::stl::vector<uint8_t>& m_buffer;
|
||||
uint32_t m_offset;
|
||||
uint32_t m_endOffset;
|
||||
bool m_zeroCopy; // if true, returns a pointer to the original buffer in memory
|
||||
public:
|
||||
reader( const uf::stl::vector<uint8_t>& buffer, uint32_t offset, uint32_t length, bool zeroCopy = true );
|
||||
|
||||
inline bool eof() const { return m_offset >= m_endOffset; }
|
||||
inline uint32_t offset() const { return m_offset; }
|
||||
inline uint32_t remaining() const { return m_endOffset - m_offset; }
|
||||
inline void skip( size_t bytes ) { m_offset += bytes; }
|
||||
|
||||
template<typename T>
|
||||
const T* read( size_t readSize = sizeof(T) ) {
|
||||
if ( m_offset + readSize > m_endOffset ) return nullptr;
|
||||
if ( !m_zeroCopy ) {
|
||||
static thread_local T copy;
|
||||
memset(©, 0, sizeof(T));
|
||||
size_t copySize = std::min(sizeof(T), readSize);
|
||||
memcpy(©, m_buffer.data() + m_offset, copySize);
|
||||
m_offset += readSize;
|
||||
return ©
|
||||
}
|
||||
|
||||
const T* ptr = (const T*)(m_buffer.data() + m_offset);
|
||||
m_offset += readSize;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool read( size_t count, uf::stl::vector<T>& outArray ) {
|
||||
size_t bytes = count * sizeof(T);
|
||||
if ( m_offset + bytes > m_endOffset ) {
|
||||
bytes = m_endOffset - m_offset;
|
||||
count = bytes / sizeof(T);
|
||||
if (count == 0) return false;
|
||||
bytes = count * sizeof(T);
|
||||
}
|
||||
|
||||
if ( !m_zeroCopy ) {
|
||||
outArray.resize(count);
|
||||
memcpy(outArray.data(), m_buffer.data() + m_offset, bytes);
|
||||
} else {
|
||||
outArray.assign(
|
||||
(const T*)(m_buffer.data() + m_offset),
|
||||
(const T*)(m_buffer.data() + m_offset + bytes)
|
||||
);
|
||||
}
|
||||
m_offset += bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
// to-do: #if C++ >= 20 or something
|
||||
template<typename T>
|
||||
std::span<const T> view( size_t count ) {
|
||||
size_t bytes = count * sizeof(T);
|
||||
if (m_offset + bytes > m_endOffset) return {};
|
||||
std::span<const T> view((const T*)(m_buffer.data() + m_offset), count);
|
||||
m_offset += bytes;
|
||||
return view;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* peek() const {
|
||||
if ( m_offset + sizeof(T) > m_endOffset ) return nullptr;
|
||||
return (const T*)(m_buffer.data() + m_offset);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template<> const char* uf::stl::reader::read<char>( size_t readSize );
|
||||
@ -44,6 +44,7 @@ void ext::AudioEmitterBehavior::initialize( uf::Object& self ) {
|
||||
if ( ext::json::isNull(json["gain"]) ) json["gain"] = metadataJson["audio"]["gain"];
|
||||
if ( ext::json::isNull(json["rolloffFactor"]) ) json["rolloffFactor"] = metadataJson["audio"]["rolloffFactor"];
|
||||
if ( ext::json::isNull(json["maxDistance"]) ) json["maxDistance"] = metadataJson["audio"]["maxDistance"];
|
||||
if ( ext::json::isNull(json["referenceDistance"]) ) json["referenceDistance"] = metadataJson["audio"]["referenceDistance"];
|
||||
if ( ext::json::isNull(json["epsilon"]) ) json["epsilon"] = metadataJson["audio"]["epsilon"];
|
||||
if ( ext::json::isNull(json["loop"]) ) json["loop"] = metadataJson["audio"]["loop"];
|
||||
if ( ext::json::isNull(json["streamed"]) ) json["streamed"] = metadataJson["audio"]["streamed"];
|
||||
@ -68,6 +69,7 @@ void ext::AudioEmitterBehavior::initialize( uf::Object& self ) {
|
||||
if ( json["gain"].is<double>() ) uf::audio::gain(source, json["gain"].as<float>());
|
||||
if ( json["rolloffFactor"].is<double>() ) uf::audio::rolloff(source, json["rolloffFactor"].as<float>());
|
||||
if ( json["maxDistance"].is<double>() ) uf::audio::maxDistance(source, json["maxDistance"].as<float>());
|
||||
if ( json["referenceDistance"].is<double>() ) uf::audio::referenceDistance(source, json["referenceDistance"].as<float>());
|
||||
if ( json["spatial"].is<bool>() ) source.settings.spatial = json["spatial"].as<bool>();
|
||||
|
||||
if ( json["loop"].is<bool>() ) uf::audio::loop(source, json["loop"].as<bool>());
|
||||
@ -97,7 +99,6 @@ void ext::AudioEmitterBehavior::initialize( uf::Object& self ) {
|
||||
uf::stl::string filename = payload["filename"].as<uf::stl::string>();
|
||||
int layer = payload["layer"].as<int>(1);
|
||||
uf::stl::string channelName = "managed_bgm_channel_" + std::to_string(layer);
|
||||
UF_MSG_DEBUG("filename={}, channelName={}", filename, channelName);
|
||||
|
||||
if ( emitter.has( channelName ) ) {
|
||||
auto& source = emitter.get(channelName);
|
||||
@ -159,8 +160,6 @@ void ext::AudioEmitterBehavior::initialize( uf::Object& self ) {
|
||||
|
||||
metadata.current = payload.uri;
|
||||
track.active = true;
|
||||
|
||||
UF_MSG_DEBUG("Playing: {} (epsilon: {})", metadata.current, track.epsilon);
|
||||
} else {
|
||||
ext::json::Value json = metadataJson["audio"];
|
||||
json["filename"] = payload.filename;
|
||||
@ -259,7 +258,6 @@ void ext::AudioEmitterBehavior::tick( uf::Object& self ) {
|
||||
if ( metadata.tracks.count( metadata.current ) > 0 ) {
|
||||
auto& track = metadata.tracks[metadata.current];
|
||||
if ( track.active && !bgmFound ) {
|
||||
UF_MSG_DEBUG("BGM source vanished! Firing TrackEnded natively.");
|
||||
track.active = false;
|
||||
|
||||
bool isIntro = metadata.current == track.intro;
|
||||
|
||||
@ -823,6 +823,7 @@ void uf::graph::process( pod::Graph& graph ) {
|
||||
auto& sceneMetadataJson = scene.getComponent<uf::Serializer>();
|
||||
auto& graphMetadataJson = graph.metadata;
|
||||
auto& graphMetadataValve = graphMetadataJson["valve"];
|
||||
auto& graphMetadataDark = graphMetadataJson["dark"];
|
||||
auto& storage = uf::graph::getStorage( graph );
|
||||
|
||||
std::lock_guard<std::mutex> lock(*storage.mutex);
|
||||
@ -1436,6 +1437,22 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
// convert metadata["dark"] into internal values:
|
||||
auto& metadataDark = node.metadata["dark"];
|
||||
if ( ext::json::isObject( metadataDark ) ) {
|
||||
bool emitsAudio = false;
|
||||
|
||||
// bind elevator
|
||||
bool isElevator = false;
|
||||
if ( ext::json::isArray( metadataDark["scripts"] ) ) {
|
||||
ext::json::forEach( metadataDark["scripts"], [&]( ext::json::Value& value ){
|
||||
auto script = value.as<uf::stl::string>();
|
||||
if ( script == "BaseElevator" || script == "Elevator" ) isElevator = true;
|
||||
});
|
||||
}
|
||||
|
||||
if ( metadataDark["class_tags"].is<uf::stl::string>() ) {
|
||||
emitsAudio = true;
|
||||
} else if ( ext::json::isObject( metadataDark["sound"] ) ) {
|
||||
emitsAudio = true;
|
||||
}
|
||||
|
||||
// bind io connectivity
|
||||
if ( ext::json::isArray( metadataDark["connections"] ) || metadataDark["id"].is<int>() ) {
|
||||
@ -1446,27 +1463,28 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
// bind door
|
||||
if ( ext::json::isObject( metadataDark["door"] ) ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/dark/door.lua");
|
||||
loadJson["behaviors"].emplace_back("AudioEmitterBehavior");
|
||||
emitsAudio = true;
|
||||
}
|
||||
|
||||
node.metadata["dark"]["schema_db"] = graph.metadata["dark"]["schema_db"];
|
||||
if ( isElevator ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/dark/elevator.lua");
|
||||
emitsAudio = true;
|
||||
}
|
||||
|
||||
// bind songs
|
||||
if ( ext::json::isObject( metadataDark["songs"] ) ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/dark/music.lua");
|
||||
loadJson["behaviors"].emplace_back("AudioEmitterBehavior");
|
||||
}
|
||||
|
||||
// bind ambient
|
||||
if ( ext::json::isObject( metadataDark["sound"] ) ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/dark/ambient.lua");
|
||||
loadJson["behaviors"].emplace_back("AudioEmitterBehavior");
|
||||
emitsAudio = true;
|
||||
}
|
||||
|
||||
// bind trap
|
||||
if ( node.name.find("SoundTrap") != uf::stl::string::npos || node.name.find("Votrap") != uf::stl::string::npos ) {
|
||||
if ( node.name == "Sound Trap" || node.name == "VO Trap" ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/dark/trap.lua");
|
||||
loadJson["behaviors"].emplace_back("AudioEmitterBehavior");
|
||||
emitsAudio = true;
|
||||
// bind ambient
|
||||
} else if ( ext::json::isObject( metadataDark["sound"] ) ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/dark/ambient.lua");
|
||||
emitsAudio = true;
|
||||
}
|
||||
|
||||
// bind trigger
|
||||
@ -1474,6 +1492,55 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
if ( ext::json::isObject( physMeta ) && physMeta["category"].as<uf::stl::string>("") == "trigger" ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/dark/trigger.lua");
|
||||
}
|
||||
|
||||
// bind schema based on what we need
|
||||
uf::stl::string classTags = metadataDark["class_tags"].as<uf::stl::string>("");
|
||||
uf::stl::string explicitSchema = "";
|
||||
if ( metadataDark["sound"].isObject() ) {
|
||||
explicitSchema = metadataDark["sound"]["schema"].as<uf::stl::string>("");
|
||||
}
|
||||
|
||||
if ( !classTags.empty() || !explicitSchema.empty() ) {
|
||||
auto& localDb = node.metadata["dark"]["schema_db"];
|
||||
|
||||
uf::stl::vector<uf::stl::string> searchTerms;
|
||||
auto extractTerm = [&](const uf::stl::string& prefix) {
|
||||
size_t p = classTags.find(prefix);
|
||||
if ( p != uf::stl::string::npos ) {
|
||||
p += prefix.length();
|
||||
size_t end = classTags.find_first_of(" ,", p);
|
||||
if ( end == uf::stl::string::npos ) end = classTags.length();
|
||||
uf::stl::string term = classTags.substr(p, end - p);
|
||||
if ( !term.empty() ) searchTerms.push_back(term);
|
||||
}
|
||||
};
|
||||
|
||||
extractTerm("DeviceType ");
|
||||
extractTerm("DoorType ");
|
||||
|
||||
ext::json::forEach( graph.metadata["dark"]["schema_db"], [&]( ext::json::Value& sch ){
|
||||
uf::stl::string sName = sch["name"].as<uf::stl::string>("");
|
||||
uf::stl::string sTags = sch["tags"].as<uf::stl::string>("");
|
||||
bool keep = false;
|
||||
|
||||
if ( !explicitSchema.empty() && sName == explicitSchema ) {
|
||||
keep = true;
|
||||
} else {
|
||||
for ( const auto& term : searchTerms ) {
|
||||
if ( sTags.find(term) != uf::stl::string::npos ) {
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( keep ) {
|
||||
localDb.emplace_back(sch);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ( emitsAudio ) loadJson["behaviors"].emplace_back("AudioEmitterBehavior");
|
||||
}
|
||||
|
||||
if ( ext::json::isObject( tag ) ) {
|
||||
@ -1579,7 +1646,9 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
auto& transform = entity.getComponent<pod::Transform<>>();
|
||||
{
|
||||
transform = node.transform;
|
||||
transform.reference = &parent.getComponent<pod::Transform<>>();
|
||||
if ( node.metadata["debug"]["parent node transforms"].as<bool>(true) ) {
|
||||
transform.reference = &parent.getComponent<pod::Transform<>>();
|
||||
}
|
||||
// override transform
|
||||
if ( tag["transform"]["offset"].as<bool>() ) {
|
||||
auto parsed = uf::transform::decode( tag["transform"], pod::Transform<>{} );
|
||||
|
||||
@ -282,11 +282,18 @@ namespace binds {
|
||||
void ext::lua::initialize() {
|
||||
if ( !ext::lua::enabled ) return;
|
||||
|
||||
state.open_libraries(
|
||||
sol::lib::base
|
||||
,sol::lib::package
|
||||
,sol::lib::table
|
||||
,sol::lib::math
|
||||
,sol::lib::string
|
||||
,sol::lib::bit32
|
||||
#if UF_USE_LUAJIT
|
||||
state.open_libraries(sol::lib::base, sol::lib::package, sol::lib::table, sol::lib::math, sol::lib::string, sol::lib::ffi, sol::lib::jit);
|
||||
#else
|
||||
state.open_libraries(sol::lib::base, sol::lib::package, sol::lib::table, sol::lib::math, sol::lib::string);
|
||||
,sol::lib::ffi
|
||||
,sol::lib::jit
|
||||
#endif
|
||||
);
|
||||
|
||||
// load modules
|
||||
for ( auto pair : modules ) {
|
||||
|
||||
@ -44,8 +44,8 @@ namespace binds {
|
||||
return sol::as_table( events );
|
||||
}
|
||||
|
||||
std::tuple<uf::Object*, float> rayCast( pod::PhysicsBody& self, const pod::Vector3f& center, const pod::Vector3f& direction ) {
|
||||
pod::RayQuery query = uf::physics::rayCast( pod::Ray{center, uf::vector::normalize( direction )}, self, uf::vector::norm( direction ) );
|
||||
std::tuple<uf::Object*, float> rayCast( pod::PhysicsBody& self, const pod::Vector3f& center, const pod::Vector3f& direction, sol::optional<uint32_t> mask ) {
|
||||
pod::RayQuery query = uf::physics::rayCast( pod::Ray{center, uf::vector::normalize( direction )}, self, uf::vector::norm( direction ), mask.value_or(pod::Collider::MASK_PHYSICAL) );
|
||||
uf::Object* object = query.hit ? query.body->object : NULL;
|
||||
float depth = query.hit ? query.contact.penetration : -1;
|
||||
return std::make_tuple( object, depth );
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include <uf/ext/ttlg/common.h>
|
||||
#include <uf/ext/valve/common.h>
|
||||
#include <uf/ext/zlib/zlib.h>
|
||||
#include <cstring>
|
||||
#include <uf/utils/memory/reader.h>
|
||||
|
||||
namespace impl {
|
||||
#pragma pack(push, 1)
|
||||
@ -137,13 +137,14 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t offset = 0;
|
||||
uf::stl::reader reader(buffer, 0, buffer.size());
|
||||
|
||||
impl::BinMainHeader mainHeader;
|
||||
if ( !impl::readStruct( buffer, offset, mainHeader ) ) {
|
||||
const auto* pMainHeader = reader.read<impl::BinMainHeader>();
|
||||
if ( !pMainHeader ) {
|
||||
UF_MSG_ERROR("Failed to read BIN header: {}", filename);
|
||||
return false;
|
||||
}
|
||||
impl::BinMainHeader mainHeader = *pMainHeader;
|
||||
|
||||
if ( strncmp(mainHeader.magic, "LGMM", 4) == 0 ) {
|
||||
UF_MSG_ERROR("Attempting to read LGMM file as an LMGD: {}", filename);
|
||||
@ -155,14 +156,15 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
impl::BinHeader header;
|
||||
if ( !impl::readStruct(buffer, offset, header ) ) {
|
||||
const auto* pHeader = reader.read<impl::BinHeader>();
|
||||
if ( !pHeader ) {
|
||||
UF_MSG_ERROR("Failed to parse BIN header: {}", filename);
|
||||
return false;
|
||||
}
|
||||
impl::BinHeader header = *pHeader;
|
||||
|
||||
// skip additional information (to-do: parse)
|
||||
if ( mainHeader.version == 4 ) offset += 12;
|
||||
if ( mainHeader.version == 4 ) reader.skip(12);
|
||||
|
||||
if ( header.offset_poly_list == 0 || header.offset_poly_list >= buffer.size() ) {
|
||||
UF_MSG_ERROR("Invalid BIN file (poly list offset out of bounds): {}", filename);
|
||||
@ -184,10 +186,10 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
|
||||
// read materials
|
||||
if ( header.num_mats > 0 && header.offset_mats > 0 && header.offset_mats < buffer.size() ) {
|
||||
uint32_t matOffset = header.offset_mats;
|
||||
uf::stl::reader matReader(buffer, header.offset_mats, buffer.size() - header.offset_mats);
|
||||
uf::stl::vector<impl::BinMaterial> binMats;
|
||||
|
||||
if ( impl::readArray(buffer, matOffset, header.num_mats, binMats ) ) {
|
||||
if ( matReader.read(header.num_mats, binMats ) ) {
|
||||
for ( uint32_t i = 0; i < header.num_mats; ++i ) {
|
||||
uf::stl::string matName = uf::stl::string(binMats[i].name);
|
||||
if ( matName.empty() ) {
|
||||
@ -200,7 +202,6 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
if ( dotPos != uf::stl::string::npos ) matName = matName.substr(0, dotPos);
|
||||
}
|
||||
|
||||
|
||||
auto it = std::find(graph.materials.begin(), graph.materials.end(), matName);
|
||||
int32_t matIndex = (mainHeader.version == 4) ? i : binMats[i].slot_num;
|
||||
|
||||
@ -217,29 +218,29 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
|
||||
// read vertices
|
||||
if ( header.num_verts > 0 && header.offset_verts > 0 && header.offset_verts < buffer.size() ) {
|
||||
uint32_t offset = header.offset_verts;
|
||||
impl::readArray( buffer, offset, header.num_verts, vertices );
|
||||
uf::stl::reader vertReader(buffer, header.offset_verts, buffer.size() - header.offset_verts);
|
||||
vertReader.read(header.num_verts, vertices);
|
||||
}
|
||||
// read UVs
|
||||
if ( header.offset_uv > 0 && header.offset_uv < buffer.size() ) {
|
||||
numUVs = (buffer.size() - header.offset_uv) / sizeof(impl::BinUV);
|
||||
}
|
||||
if ( numUVs > 0 ) {
|
||||
uint32_t offset = header.offset_uv;
|
||||
impl::readArray( buffer, offset, numUVs, uvs );
|
||||
uf::stl::reader uvReader(buffer, header.offset_uv, buffer.size() - header.offset_uv);
|
||||
uvReader.read(numUVs, uvs);
|
||||
}
|
||||
// read normals
|
||||
if ( header.offset_norms > 0 && header.offset_norms < buffer.size() ) {
|
||||
numNormals = (buffer.size() - header.offset_norms) / sizeof(pod::Vector3f);
|
||||
}
|
||||
if ( numNormals > 0 ) {
|
||||
uint32_t offset = header.offset_norms;
|
||||
impl::readArray( buffer, offset, numNormals, normals );
|
||||
uf::stl::reader normReader(buffer, header.offset_norms, buffer.size() - header.offset_norms);
|
||||
normReader.read(numNormals, normals);
|
||||
}
|
||||
// read subobject information
|
||||
if ( header.num_objs > 0 && header.offset_objs > 0 && header.offset_objs < buffer.size() ) {
|
||||
uint32_t offset = header.offset_objs;
|
||||
impl::readArray( buffer, offset, header.num_objs, subObjects );
|
||||
uf::stl::reader objReader(buffer, header.offset_objs, buffer.size() - header.offset_objs);
|
||||
objReader.read(header.num_objs, subObjects);
|
||||
}
|
||||
|
||||
uf::stl::vector<pod::Matrix4f> transforms( subObjects.size(), uf::matrix::identity() );
|
||||
@ -250,43 +251,59 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
|
||||
// read subobject faces
|
||||
if ( header.offset_nodes > 0 && header.offset_nodes < buffer.size() ) {
|
||||
uint32_t offset = header.offset_nodes;
|
||||
uf::stl::reader nodeReader(buffer, header.offset_nodes, buffer.size() - header.offset_nodes);
|
||||
uf::stl::vector<uint16_t> faces;
|
||||
while ( offset < buffer.size() ) {
|
||||
|
||||
while ( !nodeReader.eof() ) {
|
||||
faces.clear();
|
||||
uint8_t nodeType = buffer[offset];
|
||||
|
||||
const auto* pNodeType = nodeReader.peek<uint8_t>();
|
||||
if (!pNodeType) break;
|
||||
uint8_t nodeType = *pNodeType;
|
||||
|
||||
if ( nodeType == 4 ) {
|
||||
subObjectPolys.emplace_back();
|
||||
offset += 3;
|
||||
nodeReader.skip(3);
|
||||
} else if ( nodeType == 3 ) {
|
||||
offset += 19;
|
||||
nodeReader.skip(19);
|
||||
} else if ( nodeType == 2 ) {
|
||||
uint16_t nf1{}, nf2{};
|
||||
offset += 17;
|
||||
impl::readStruct( buffer, offset, nf1 );
|
||||
offset += 2;
|
||||
impl::readStruct( buffer, offset, nf2 );
|
||||
nodeReader.skip(17);
|
||||
|
||||
if ( impl::readArray( buffer, offset, nf1 + nf2, faces ) && !subObjectPolys.empty() ) {
|
||||
const auto* pNf1 = nodeReader.read<uint16_t>();
|
||||
if (!pNf1) break;
|
||||
uint16_t nf1 = *pNf1;
|
||||
|
||||
nodeReader.skip(2);
|
||||
const auto* pNf2 = nodeReader.read<uint16_t>();
|
||||
if (!pNf2) break;
|
||||
uint16_t nf2 = *pNf2;
|
||||
|
||||
if ( nodeReader.read(nf1 + nf2, faces) && !subObjectPolys.empty() ) {
|
||||
subObjectPolys.back().insert( subObjectPolys.back().end(), faces.begin(), faces.end() );
|
||||
}
|
||||
} else if (nodeType == 1) {
|
||||
uint16_t nf1{}, nf2{};
|
||||
offset += 17;
|
||||
impl::readStruct( buffer, offset, nf1 );
|
||||
offset += 10;
|
||||
impl::readStruct( buffer, offset, nf2 );
|
||||
nodeReader.skip(17);
|
||||
|
||||
if ( impl::readArray( buffer, offset, nf1 + nf2, faces ) && !subObjectPolys.empty() ) {
|
||||
const auto* pNf1 = nodeReader.read<uint16_t>();
|
||||
if (!pNf1) break;
|
||||
uint16_t nf1 = *pNf1;
|
||||
|
||||
nodeReader.skip(10);
|
||||
const auto* pNf2 = nodeReader.read<uint16_t>();
|
||||
if (!pNf2) break;
|
||||
uint16_t nf2 = *pNf2;
|
||||
|
||||
if ( nodeReader.read(nf1 + nf2, faces) && !subObjectPolys.empty() ) {
|
||||
subObjectPolys.back().insert( subObjectPolys.back().end(), faces.begin(), faces.end() );
|
||||
}
|
||||
} else if (nodeType == 0) {
|
||||
uint16_t nf{};
|
||||
offset += 17;
|
||||
impl::readStruct( buffer, offset, nf );
|
||||
nodeReader.skip(17);
|
||||
|
||||
if ( impl::readArray(buffer, offset, nf, faces) && !subObjectPolys.empty() ) {
|
||||
const auto* pNf = nodeReader.read<uint16_t>();
|
||||
if (!pNf) break;
|
||||
uint16_t nf = *pNf;
|
||||
|
||||
if ( nodeReader.read(nf, faces) && !subObjectPolys.empty() ) {
|
||||
subObjectPolys.back().insert(subObjectPolys.back().end(), faces.begin(), faces.end());
|
||||
}
|
||||
} else {
|
||||
@ -308,27 +325,26 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
uint32_t absolutePolyOffset = header.offset_poly_list + polyOffset;
|
||||
if (absolutePolyOffset >= buffer.size()) continue;
|
||||
|
||||
impl::BinPolyHeader polyHeader;
|
||||
uint32_t readOffset = absolutePolyOffset;
|
||||
if (!impl::readStruct(buffer, readOffset, polyHeader)) continue;
|
||||
uf::stl::reader polyReader(buffer, absolutePolyOffset, buffer.size() - absolutePolyOffset);
|
||||
|
||||
uint32_t dataOffset = absolutePolyOffset + 12;
|
||||
const auto* pPolyHeader = polyReader.read<impl::BinPolyHeader>();
|
||||
if (!pPolyHeader) continue;
|
||||
impl::BinPolyHeader polyHeader = *pPolyHeader;
|
||||
|
||||
uf::stl::vector<uint16_t> vertIndices;
|
||||
impl::readArray(buffer, dataOffset, polyHeader.num_verts, vertIndices);
|
||||
polyReader.read(polyHeader.num_verts, vertIndices);
|
||||
|
||||
uf::stl::vector<uint16_t> normIndices;
|
||||
impl::readArray(buffer, dataOffset, polyHeader.num_verts, normIndices);
|
||||
polyReader.read(polyHeader.num_verts, normIndices);
|
||||
|
||||
uf::stl::vector<uint16_t> uvIndices;
|
||||
bool hasUVs = ((polyHeader.type & 3) == 3);
|
||||
if (hasUVs) impl::readArray(buffer, dataOffset, polyHeader.num_verts, uvIndices);
|
||||
if (hasUVs) polyReader.read(polyHeader.num_verts, uvIndices);
|
||||
|
||||
uint32_t localMatID = 0;
|
||||
if ( mainHeader.version == 4 ) {
|
||||
uint8_t matByte = 0;
|
||||
impl::readStruct(buffer, dataOffset, matByte);
|
||||
localMatID = matByte;
|
||||
const auto* matByte = polyReader.read<uint8_t>();
|
||||
if (matByte) localMatID = *matByte;
|
||||
} else {
|
||||
localMatID = polyHeader.data;
|
||||
}
|
||||
@ -351,7 +367,9 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
|
||||
for (uint8_t v = 0; v < polyHeader.num_verts; ++v) {
|
||||
auto& vert = meshlet.vertices.emplace_back();
|
||||
uint16_t vIdx = vertIndices[v];
|
||||
|
||||
uint16_t vIdx = 0;
|
||||
if (v < vertIndices.size()) vIdx = vertIndices[v]; // Bounds safety
|
||||
|
||||
if ( vIdx < vertices.size() ) {
|
||||
vert.position = impl::convertPos_NewDark( uf::matrix::multiply<float>(transforms[objIdx], vertices[vIdx].position, 1.0f) + offsets[objIdx] );
|
||||
@ -366,10 +384,12 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
}
|
||||
|
||||
// triangle fan => triangles
|
||||
for ( uint8_t v = 1; v < polyHeader.num_verts - 1; ++v ) {
|
||||
meshlet.indices.emplace_back(startVertIdx);
|
||||
meshlet.indices.emplace_back(startVertIdx + v);
|
||||
meshlet.indices.emplace_back(startVertIdx + v + 1);
|
||||
if (polyHeader.num_verts >= 3) {
|
||||
for ( uint8_t v = 1; v < polyHeader.num_verts - 1; ++v ) {
|
||||
meshlet.indices.emplace_back(startVertIdx);
|
||||
meshlet.indices.emplace_back(startVertIdx + v);
|
||||
meshlet.indices.emplace_back(startVertIdx + v + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -377,12 +397,6 @@ bool ext::ttlg::loadBin( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
if ( meshlets.empty() ) {
|
||||
if ( header.num_vhots > 0 || header.num_objs > 0 || header.num_polys == 0 ) {
|
||||
UF_MSG_DEBUG("BIN file acts as a dummy node (no polygons, but has VHOTs/Objs): {}", filename);
|
||||
/*
|
||||
uf::stl::string meshName = filename;
|
||||
graph.meshes.emplace_back(meshName);
|
||||
graph.primitives.emplace_back(meshName);
|
||||
return true;
|
||||
*/
|
||||
} else {
|
||||
UF_MSG_WARNING("BIN file contained no valid polygons: {}", filename);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -228,6 +228,15 @@ void uf::audio::maxDistance( pod::AudioSource& source, float v ) {
|
||||
source.alSource.set( AL_MAX_DISTANCE, v );
|
||||
}
|
||||
|
||||
float uf::audio::referenceDistance( const pod::AudioSource& source ) {
|
||||
float v;
|
||||
source.alSource.get( AL_REFERENCE_DISTANCE, v );
|
||||
return v;
|
||||
}
|
||||
void uf::audio::referenceDistance( pod::AudioSource& source, float v ) {
|
||||
source.alSource.set( AL_REFERENCE_DISTANCE, v );
|
||||
}
|
||||
|
||||
//
|
||||
float uf::audio::distance( const pod::Vector3f& position ) {
|
||||
return uf::vector::distance( ::listener.position, position );
|
||||
|
||||
@ -323,6 +323,7 @@ void uf::physics::setColliderCategory( pod::PhysicsBody& body, const uf::stl::st
|
||||
if ( c == "NPC" ) return uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_NPC );
|
||||
if ( c == "TRIGGER" ) return uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_TRIGGER );
|
||||
if ( c == "PROJECTILE" ) return uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_PROJECTILE );
|
||||
if ( c == "INTERACTABLE" ) return uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_INTERACTABLE );
|
||||
if ( c == "CHARACTER" ) return uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_CHARACTER );
|
||||
if ( c == "ALL" ) return uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_ALL );
|
||||
}
|
||||
@ -339,6 +340,7 @@ void uf::physics::setColliderMask( pod::PhysicsBody& body, const uf::stl::string
|
||||
if ( m == "TRIGGER" ) return uf::physics::setColliderMask( body, pod::Collider::MASK_TRIGGER );
|
||||
if ( m == "PROJECTILE" ) return uf::physics::setColliderMask( body, pod::Collider::MASK_PROJECTILE );
|
||||
if ( m == "CHARACTER" ) return uf::physics::setColliderMask( body, pod::Collider::MASK_CHARACTER );
|
||||
if ( m == "PHYSICAL" ) return uf::physics::setColliderMask( body, pod::Collider::MASK_PHYSICAL );
|
||||
if ( m == "ALL" ) return uf::physics::setColliderMask( body, pod::Collider::MASK_ALL );
|
||||
}
|
||||
void uf::physics::setGravity( pod::PhysicsBody& body, const pod::Vector3f& gravity ) {
|
||||
|
||||
@ -309,13 +309,13 @@ void impl::drawRay( const pod::Ray& ray, const pod::RayQuery& query ) {
|
||||
uf::debug::addLine( start, end, query.hit ? pod::Vector4f{ 0, 1, 0, 1 } : pod::Vector4f{ 1, 0, 0, 1 } );
|
||||
}
|
||||
|
||||
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::PhysicsBody& body, float maxDistance ) {
|
||||
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::PhysicsBody& body, float maxDistance, uint32_t mask ) {
|
||||
return rayCast( ray, body.world ? *body.world : uf::physics::getWorld(), &body, maxDistance );
|
||||
}
|
||||
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world, float maxDistance ) {
|
||||
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world, float maxDistance, uint32_t mask ) {
|
||||
return rayCast( ray, world, NULL, maxDistance );
|
||||
}
|
||||
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world, const pod::PhysicsBody* body, float maxDistance ) {
|
||||
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world, const pod::PhysicsBody* body, float maxDistance, uint32_t mask ) {
|
||||
pod::RayQuery rayHit;
|
||||
rayHit.invoker = body;
|
||||
rayHit.contact.penetration = maxDistance;
|
||||
@ -334,6 +334,8 @@ pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world
|
||||
|
||||
if ( body == b ) continue;
|
||||
|
||||
if ( !(b->collider.category & mask) ) continue;
|
||||
|
||||
switch ( b->collider.type ) {
|
||||
case pod::ShapeType::AABB: impl::rayAabb( ray, *b, rayHit ); break;
|
||||
case pod::ShapeType::OBB: impl::rayObb( ray, *b, rayHit ); break;
|
||||
@ -361,7 +363,7 @@ float uf::physics::occlusion( const pod::Vector3f& to, const pod::Vector3f& from
|
||||
ray.origin = from;
|
||||
ray.direction = dir;
|
||||
|
||||
pod::RayQuery hit = uf::physics::rayCast( ray, uf::physics::getWorld(), dist );
|
||||
pod::RayQuery hit = uf::physics::rayCast( ray, uf::physics::getWorld(), dist, pod::Collider::MASK_PHYSICAL );
|
||||
|
||||
if ( hit.contact.penetration >= dist ) return 1.0f;
|
||||
|
||||
@ -379,7 +381,7 @@ pod::AcousticBounce uf::physics::acousticReflection( const pod::Vector3f& source
|
||||
primaryRay.origin = sourcePos;
|
||||
primaryRay.direction = rayDirection;
|
||||
|
||||
pod::RayQuery firstHit = uf::physics::rayCast( primaryRay, uf::physics::getWorld(), maxDistance );
|
||||
pod::RayQuery firstHit = uf::physics::rayCast( primaryRay, uf::physics::getWorld(), maxDistance, pod::Collider::MASK_PHYSICAL );
|
||||
|
||||
if ( firstHit.contact.penetration >= maxDistance ) return result;
|
||||
|
||||
@ -403,7 +405,7 @@ pod::AcousticBounce uf::physics::acousticReflection( const pod::Vector3f& source
|
||||
secondaryRay.origin = bounceOrigin;
|
||||
secondaryRay.direction = toListener / distToListener;
|
||||
|
||||
pod::RayQuery secondHit = uf::physics::rayCast( secondaryRay, uf::physics::getWorld(), distToListener );
|
||||
pod::RayQuery secondHit = uf::physics::rayCast( secondaryRay, uf::physics::getWorld(), distToListener, pod::Collider::MASK_PHYSICAL );
|
||||
|
||||
if ( secondHit.contact.penetration >= distToListener ) {
|
||||
result.valid = true;
|
||||
|
||||
14
engine/src/utils/memory/reader.cpp
Normal file
14
engine/src/utils/memory/reader.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
#include <uf/utils/memory/reader.h>
|
||||
|
||||
uf::stl::reader::reader( const uf::stl::vector<uint8_t>& buffer, uint32_t offset, uint32_t length, bool zeroCopy ) : m_buffer(buffer), m_offset(offset), m_endOffset(offset + length), m_zeroCopy( zeroCopy ) {
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
const char* uf::stl::reader::read<char>( size_t readSize ) {
|
||||
if ( m_offset + readSize > m_endOffset ) return nullptr;
|
||||
|
||||
const char* ptr = (const char*)(m_buffer.data() + m_offset);
|
||||
m_offset += readSize;
|
||||
return ptr;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user