more bug fixes (fixed that weird instancing bug where things get rendered at origin because of instanced graphics also rendering, mdl fixes, fixed lightmaps with displacements, fixed trigger physics bodies)
This commit is contained in:
parent
e4ad164203
commit
6ae24419e7
1
.gitignore
vendored
1
.gitignore
vendored
@ -63,6 +63,7 @@ default/
|
||||
*.vtf
|
||||
*.ztmp
|
||||
|
||||
maps/
|
||||
models/
|
||||
llm/
|
||||
tmp/
|
||||
@ -358,8 +358,9 @@
|
||||
"max": 0.01 // 0.2
|
||||
},
|
||||
"debug draw": {
|
||||
"static": true,
|
||||
"dynamic": true,
|
||||
"static": false,
|
||||
"dynamic": false,
|
||||
"trigger": false,
|
||||
"contacts": false,
|
||||
"constraints": true,
|
||||
"rays": false,
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"assets": ["./scripts/ambient_generic.lua"],
|
||||
"behaviors": [
|
||||
"SoundEmitterBehavior"
|
||||
]
|
||||
}
|
||||
@ -6,7 +6,6 @@
|
||||
"metadata": {
|
||||
"physics": {
|
||||
"mass": 0,
|
||||
// "inertia": [0, 0, 0],
|
||||
"type": "bounding box"
|
||||
// "type": "mesh"
|
||||
}
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"assets": ["./scripts/io.lua"]
|
||||
}
|
||||
@ -86,6 +86,8 @@
|
||||
"type": "capsule",
|
||||
"radius": 1,
|
||||
"height": 2,
|
||||
"category": "player",
|
||||
"mask": "player",
|
||||
|
||||
// "type": "bounding box",
|
||||
// "min": [ -1, -1, -1 ],
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
local ent = ent
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local metadataVale = metadata["valve"] or {}
|
||||
local metadataValve = metadata["valve"] or {}
|
||||
|
||||
local soundFile = metadataVale["message"] or ""
|
||||
local flags = metadataVale["spawnflags"] or 0
|
||||
local soundFile = metadataValve["message"] or ""
|
||||
local flags = metadataValve["spawnflags"] or 0
|
||||
|
||||
local volume = tonumber(metadataVale["health"]) or 10.0
|
||||
local volume = tonumber(metadataValve["health"]) or 10.0
|
||||
volume = volume / 10.0
|
||||
|
||||
local playEverywhere = (math.floor(flags / 1) % 2) ~= 0
|
||||
|
||||
@ -38,7 +38,7 @@ ent:addHook("io:Input.%UID%", function(payload)
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnUser3" })
|
||||
elseif input == "FireUser4" then
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnUser4" })
|
||||
-- colors
|
||||
-- colors
|
||||
elseif input == "Alpha" then
|
||||
local alphaVal = tonumber(param) / 255.0
|
||||
if alphaVal then
|
||||
@ -73,8 +73,7 @@ ent:bind("tick", function(self)
|
||||
|
||||
for i = #pendingOutputs, 1, -1 do
|
||||
local job = pendingOutputs[i]
|
||||
if currentTime >= job.fireTime then
|
||||
|
||||
if currentTime >= job.fireTime then
|
||||
local targetUIDs = _G.IOTargets[job.target]
|
||||
if targetUIDs then
|
||||
for _, targetUID in ipairs(targetUIDs) do
|
||||
@ -97,18 +96,19 @@ ent:bind("tick", function(self)
|
||||
end)
|
||||
|
||||
ent:addHook("io:FireOutput.%UID%", function(payload)
|
||||
local outputName = payload.output
|
||||
local output = payload.output
|
||||
|
||||
for i = 1, #connections do
|
||||
local conn = connections[i]
|
||||
|
||||
if conn.output == outputName then
|
||||
if conn.output == output 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,
|
||||
@ -136,8 +136,8 @@ 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" })
|
||||
ent:callHook("io:FireOutput.%UID%", { output = "OnIn" })
|
||||
end)
|
||||
@ -138,14 +138,14 @@ local function tickUse( transform, axes, inputs )
|
||||
timers.use:reset()
|
||||
local center = transform.position
|
||||
local direction = axes.forward * useDistance
|
||||
local prop, depth = physicsBody:rayCast(center, direction)
|
||||
local hit, depth = physicsBody:rayCast(center, direction)
|
||||
|
||||
local payload = {
|
||||
user = ent:uid(),
|
||||
uid = prop and prop:uid() or 0,
|
||||
uid = hit and hit:uid() or 0,
|
||||
depth = depth,
|
||||
}
|
||||
if prop then prop:lazyCallHook("entity:Use.%UID%", payload) end
|
||||
if hit then hit:lazyCallHook("entity:Use.%UID%", payload) end
|
||||
ent:lazyCallHook("entity:Use.%UID%", payload)
|
||||
end
|
||||
end
|
||||
|
||||
20
bin/data/entities/scripts/trigger.lua
Normal file
20
bin/data/entities/scripts/trigger.lua
Normal file
@ -0,0 +1,20 @@
|
||||
local ent = ent
|
||||
local scene = entities.currentScene()
|
||||
local metadata = ent:getComponent("Metadata")
|
||||
local metadataValve = metadata["valve"] or {}
|
||||
local physicsBody = ent:getComponent("PhysicsBody")
|
||||
|
||||
local timer = Timer.new()
|
||||
if not timer:running() then
|
||||
timer:start()
|
||||
end
|
||||
|
||||
--[[
|
||||
ent:bind( "tick", function(self)
|
||||
local collisionEvents = physicsBody:getCollisionEvents()
|
||||
for i, event in ipairs(collisionEvents) do
|
||||
-- do something
|
||||
-- print( event.state, event.a, event.b, event.point, event.normal, event.impulse )
|
||||
end
|
||||
end )
|
||||
]]
|
||||
@ -27,17 +27,18 @@
|
||||
"resolution": 2048
|
||||
} },*/
|
||||
|
||||
"ambient_generic": { "action": "load", "payload": { "assets": ["ent://scripts/ambient_generic.lua"], "behaviors": ["SoundEmitterBehavior"] } },
|
||||
|
||||
// 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
|
||||
"/^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" } },
|
||||
"/^prop_static/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
"/^prop_dynamic/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
"/^prop_physics/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": { "base": [ 1.0, 1.0, 1.0, 0.0 ] } }
|
||||
}
|
||||
|
||||
15
bin/data/scenes/sourceengine/cs_office.json
Normal file
15
bin/data/scenes/sourceengine/cs_office.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"import": "./base_sourceengine.json",
|
||||
"assets": [
|
||||
{ "filename": "./maps/cs_office.bsp" }
|
||||
// { "filename": "./maps/cs_office/graph.json" }
|
||||
],
|
||||
"metadata": {
|
||||
"graph": {
|
||||
// realistically shouldn't ever need to define additional tags for BSPs
|
||||
"tags": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"import": "./base_sourceengine.json",
|
||||
"assets": [
|
||||
// { "filename": "./models/mds_mcdonalds.bsp" }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" }
|
||||
{ "filename": "./maps/mds_mcdonalds.bsp" }
|
||||
// { "filename": "./maps/mds_mcdonalds/graph.json" }
|
||||
],
|
||||
"metadata": {
|
||||
"graph": {
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
{
|
||||
// "import": "./rp_downtown_v2.json"
|
||||
// "import": "./ss2_medsci1.json"
|
||||
// "import": "./test_grid.json"
|
||||
// "import": "./sh2_mcdonalds.json"
|
||||
// "import": "./animal_crossing.json"
|
||||
"import": "./mds_mcdonalds.json"
|
||||
// "import": "./mds_mcdonalds.json"
|
||||
"import": "./cs_office.json"
|
||||
// "import": "./gm_construct.json"
|
||||
}
|
||||
@ -86,9 +86,8 @@ namespace pod {
|
||||
GLOBAL,
|
||||
};
|
||||
|
||||
uf::stl::KeyMap<uf::stl::vector<pod::Instance>> groupedInstances;
|
||||
uf::stl::KeyMap<uf::stl::vector<pod::Instance::Addresses>> addresses;
|
||||
uf::stl::KeyMap<uf::stl::vector<pod::Primitive>> primitives;
|
||||
uf::stl::KeyMap<uf::stl::vector<pod::Instance>> instances;
|
||||
uf::stl::KeyMap<uf::Mesh> meshes;
|
||||
|
||||
uf::stl::KeyMap<pod::ImageTexture> images;
|
||||
|
||||
@ -333,9 +333,9 @@ namespace pod {
|
||||
enum CollisionMask : uint32_t {
|
||||
MASK_NONE = 0,
|
||||
MASK_STATIC = CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC | CATEGORY_PROJECTILE,
|
||||
MASK_DYNAMIC = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | CATEGORY_NPC,
|
||||
MASK_PLAYER = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_NPC | CATEGORY_PROJECTILE,
|
||||
MASK_NPC = CATEGORY_STATIC | CATEGORY_DYNAMIC | CATEGORY_PLAYER | 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_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,
|
||||
|
||||
@ -426,8 +426,8 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
|
||||
metadata.system.noclipped = state;
|
||||
if ( !state ) {
|
||||
uf::physics::setGravity( physicsBody );
|
||||
uf::physics::setColliderCategory( physicsBody, "DYNAMIC");
|
||||
uf::physics::setColliderMask( physicsBody, "DYNAMIC");
|
||||
uf::physics::setColliderCategory( physicsBody, "PLAYER");
|
||||
uf::physics::setColliderMask( physicsBody, "PLAYER");
|
||||
} else {
|
||||
uf::physics::setGravity( physicsBody, pod::Vector3f{0,0,0});
|
||||
uf::physics::setColliderCategory( physicsBody, "NONE");
|
||||
|
||||
@ -734,7 +734,7 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M
|
||||
// query materials if culling needs to be disabled
|
||||
for ( auto& primitive : primitives ) {
|
||||
auto materialID = primitive.instance.materialID;
|
||||
if ( 0 < materialID && materialID <= graph.materials.size() ) {
|
||||
if ( 0 <= materialID && materialID <= graph.materials.size() ) {
|
||||
auto& materialName = graph.materials[materialID];
|
||||
auto& material = storage.materials[materialName];
|
||||
if ( material.modeCull == 0 ) {
|
||||
@ -1121,7 +1121,7 @@ void uf::graph::process( pod::Graph& graph ) {
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto& instance : storage.groupedInstances.map[name] ) {
|
||||
for ( auto& instance : storage.instances.map[name] ) {
|
||||
if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) {
|
||||
auto& keys = storage.materials.keys;
|
||||
auto& indices = storage.materials.indices;
|
||||
@ -1228,18 +1228,49 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
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");
|
||||
loadJson["imports"].emplace_back("ent://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");
|
||||
loadJson["assets"].emplace_back("ent://scripts/io.lua");
|
||||
}
|
||||
|
||||
// assume all funcs are to have a physics body
|
||||
if ( node.name.starts_with("func_") ) {
|
||||
if ( ext::json::isNull( node.metadata["physics"] ) ) {
|
||||
//node.metadata["physics"]["type"] = "bounding box";
|
||||
node.metadata["physics"]["type"] = "mesh";
|
||||
node.metadata["physics"]["category"] = "trigger";
|
||||
}
|
||||
}
|
||||
|
||||
// check if trigger
|
||||
if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) {
|
||||
auto& primitives = storage.primitives.map[graph.primitives[node.mesh]];
|
||||
for ( auto& primitive : primitives ) {
|
||||
auto materialID = primitive.instance.materialID;
|
||||
if ( 0 <= materialID && materialID <= graph.materials.size() ) {
|
||||
auto& materialName = graph.materials[materialID];
|
||||
// attach trigger script + physics body
|
||||
if ( materialName == "tools/toolstrigger" ) {
|
||||
loadJson["assets"].emplace_back("ent://scripts/trigger.lua");
|
||||
// signal to assign a physics body
|
||||
if ( ext::json::isNull( node.metadata["physics"] ) ) {
|
||||
node.metadata["physics"]["type"] = "bounding box";
|
||||
node.metadata["physics"]["category"] = "trigger";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1382,18 +1413,9 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
auto primitiveName = graph.primitives[node.mesh];
|
||||
auto& primitives = storage.primitives.map[primitiveName];
|
||||
|
||||
node.object = ::allocateObjectID( storage );
|
||||
auto objectKeyName = std::to_string( node.object );
|
||||
|
||||
storage.entities[objectKeyName] = &entity;
|
||||
storage.objects[objectKeyName] = pod::Instance::Object{
|
||||
.model = model,
|
||||
.previous = model,
|
||||
};
|
||||
|
||||
pod::Instance::Bounds bounds = {};
|
||||
|
||||
auto& grouped = storage.groupedInstances[primitiveName];
|
||||
auto& grouped = storage.instances[primitiveName];
|
||||
for ( auto drawID = 0; drawID < primitives.size(); ++drawID ) {
|
||||
pod::Instance newInstance = primitives[drawID].instance;
|
||||
newInstance.objectID = node.object;
|
||||
@ -1411,41 +1433,6 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
uf::graph::initializeGraphics( graph, entity, mesh, primitives );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
auto& mesh = storage.meshes.map[graph.meshes[node.mesh]];
|
||||
auto& primitives = storage.primitives.map[graph.primitives[node.mesh]];
|
||||
|
||||
pod::Instance::Bounds bounds = {};
|
||||
size_t baseInstanceID = ::allocateInstanceID( storage, graph.primitives[node.mesh] );
|
||||
// setup instances
|
||||
for ( auto drawID = 0; drawID < primitives.size(); ++drawID ) {
|
||||
auto& primitive = primitives[drawID];
|
||||
auto& instance = primitive.instance;
|
||||
size_t instanceID = baseInstanceID + drawID;
|
||||
|
||||
instance.objectID = node.object;
|
||||
instance.jointID = graphMetadataJson["renderer"]["skinned"].as<bool>() ? 0 : -1;
|
||||
|
||||
primitive.drawCommand.instanceID = instanceID;
|
||||
|
||||
bounds.min = uf::vector::min( bounds.min, instance.bounds.min );
|
||||
bounds.max = uf::vector::max( bounds.max, instance.bounds.max );
|
||||
|
||||
if ( mesh.indirect.count && mesh.indirect.count <= primitives.size() ) {
|
||||
auto& attribute = mesh.indirect.attributes.front();
|
||||
auto& buffer = mesh.buffers[attribute.buffer];
|
||||
pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data();
|
||||
auto& drawCommand = drawCommands[drawID];
|
||||
drawCommand.instanceID = instanceID;
|
||||
}
|
||||
}
|
||||
#if !UF_GRAPH_EXTENDED
|
||||
if ( graphMetadataJson["renderer"]["render"].as<bool>() ) {
|
||||
uf::graph::initializeGraphics( graph, entity, mesh, addresses );
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
{
|
||||
auto phyziks = tag["physics"];
|
||||
@ -1608,7 +1595,7 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
if ( storage.stale ) {
|
||||
for ( auto& key : storage.primitives.keys ) {
|
||||
auto& primitives = storage.primitives.map[key];
|
||||
auto& grouped = storage.groupedInstances.map[key];
|
||||
auto& grouped = storage.instances.map[key];
|
||||
|
||||
auto& mesh = storage.meshes.map[key];
|
||||
pod::DrawCommand* commands = nullptr;
|
||||
@ -1641,11 +1628,13 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
|
||||
}
|
||||
|
||||
if ( commands && !grouped.empty() ) {
|
||||
auto hostKeyName = std::to_string(grouped.front().objectID);
|
||||
if (storage.entities.map.count(hostKeyName) > 0) {
|
||||
auto* hostEntity = storage.entities.map[hostKeyName];
|
||||
if (hostEntity && hostEntity->hasComponent<uf::renderer::Graphic>()) {
|
||||
hostEntity->getComponent<uf::renderer::Graphic>().updateMesh(mesh);
|
||||
auto objectKeyName = std::to_string(grouped.front().objectID);
|
||||
if ( storage.entities.map.count(objectKeyName) > 0 ) {
|
||||
auto& entity = *storage.entities.map[objectKeyName];
|
||||
if ( entity.hasComponent<uf::renderer::Graphic>() ) {
|
||||
auto& graphic = entity.getComponent<uf::renderer::Graphic>();
|
||||
auto& attr = mesh.indirect.attributes.front();
|
||||
graphic.updateBuffer( (const void*) attr.pointer, attr.length, graphic.metadata.buffers["indirect["+attr.descriptor.name+"]"] );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1754,12 +1743,9 @@ void uf::graph::aggregate( uf::Object& object, pod::Graph::Storage& storage ) {
|
||||
drawCommands.emplace_back( primitive.drawCommand );
|
||||
instances.emplace_back( primitive.instance );
|
||||
lodMetadata.emplace_back( primitive.lod );
|
||||
addresses.emplace_back( primitive.addresses );
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto& key : storage.addresses.keys ) {
|
||||
addresses.insert( addresses.end(), storage.addresses.map[key].begin(), storage.addresses.map[key].end() );
|
||||
}
|
||||
}
|
||||
|
||||
bool rebuild = false;
|
||||
@ -2281,8 +2267,13 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
|
||||
|
||||
storage.stale = true; // force rebuffering the draw commands
|
||||
|
||||
bool graphicOwner = graphMetadataJson["renderer"]["render"].as<bool>();
|
||||
if ( graphicOwner ) {
|
||||
auto objectKeyName = std::to_string(storage.instances.map[graph.primitives[node.mesh]].front().objectID);
|
||||
graphicOwner = storage.entities[objectKeyName] == &entity;
|
||||
}
|
||||
// update graphic
|
||||
if ( graphMetadataJson["renderer"]["render"].as<bool>() ) {
|
||||
if ( graphicOwner ) {
|
||||
bool exists = entity.hasComponent<uf::renderer::Graphic>();
|
||||
if ( exists ) {
|
||||
auto& graphic = entity.getComponent<uf::renderer::Graphic>();
|
||||
@ -2376,4 +2367,4 @@ void uf::graph::update( pod::Graph& graph, float delta ) {
|
||||
#endif
|
||||
|
||||
uf::graph::updateAnimation( graph, delta );
|
||||
}
|
||||
}
|
||||
@ -218,8 +218,8 @@ void ext::vorbis::stream(uf::Audio::Metadata& metadata) {
|
||||
}
|
||||
totalRead += result;
|
||||
}
|
||||
if (totalRead == 0) {
|
||||
UF_MSG_WARNING("Vorbis: consumed file stream before buffers are filled: {} {}", (int)queuedBuffers, metadata.filename);
|
||||
if ( totalRead == 0 ) {
|
||||
if ( queuedBuffers == 0 ) UF_MSG_WARNING("Vorbis: consumed file stream before {} buffers were filled: {}", (int)queuedBuffers, metadata.filename);
|
||||
break;
|
||||
}
|
||||
AL_CHECK_RESULT(alBufferData(metadata.al.buffer.getIndex(queuedBuffers), metadata.info.format, buffer, totalRead, metadata.info.frequency));
|
||||
|
||||
@ -216,7 +216,7 @@ void ext::wav::stream(uf::Audio::Metadata& metadata) {
|
||||
}
|
||||
|
||||
if (bytesRead == 0) {
|
||||
UF_MSG_WARNING("WAV: consumed file stream before buffers are filled: {} {}", (int)queuedBuffers, metadata.filename);
|
||||
if ( queuedBuffers == 0 ) UF_MSG_WARNING("WAV: consumed file stream before {} buffers were filled: {}", (int) queuedBuffers, metadata.filename);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -117,21 +117,46 @@ namespace impl {
|
||||
uint16_t smoothingGroups;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct BspDispSubNeighbor {
|
||||
uint16_t neighbor;
|
||||
uint8_t neighborOrientation;
|
||||
uint8_t span;
|
||||
uint8_t neighborSpan;
|
||||
uint8_t padding;
|
||||
};
|
||||
|
||||
struct BspDispNeighbor {
|
||||
BspDispSubNeighbor subNeighbors[2];
|
||||
};
|
||||
|
||||
struct BspDispCornerNeighbors {
|
||||
uint16_t neighbors[4];
|
||||
uint8_t numNeighbors;
|
||||
uint8_t padding;
|
||||
};
|
||||
|
||||
struct BspDispInfo {
|
||||
pod::Vector3f startPosition;
|
||||
int32_t dispVertStart;
|
||||
int32_t dispTriStart;
|
||||
int32_t power;
|
||||
int32_t minTess;
|
||||
int32_t maxTess;
|
||||
int32_t smoothingAngle;
|
||||
float smoothingAngle;
|
||||
int32_t contents;
|
||||
uint16_t mapFace;
|
||||
int16_t lightmapAlphaStart;
|
||||
uint16_t padding;
|
||||
int32_t lightmapAlphaStart;
|
||||
int32_t lightmapSamplePositionStart;
|
||||
uint8_t padding[130];
|
||||
|
||||
BspDispNeighbor edgeNeighbors[4];
|
||||
BspDispCornerNeighbors cornerNeighbors[4];
|
||||
uint32_t allowedVerts[10];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
struct BspDispVert {
|
||||
pod::Vector3f vec;
|
||||
float dist;
|
||||
@ -259,8 +284,8 @@ namespace impl {
|
||||
corners[i].uv.x = uf::vector::dot( v, texInfo.textureVecs[0] ) / texWidth;
|
||||
corners[i].uv.y = uf::vector::dot( v, texInfo.textureVecs[1] ) / texHeight;
|
||||
|
||||
corners[i].st.x = (uf::vector::dot( v, texInfo.lightmapVecs[0] ) - face.lightmapTextureMins.x) / (face.lightmapTextureSize.x + 1.0f);
|
||||
corners[i].st.y = (uf::vector::dot( v, texInfo.lightmapVecs[1] ) - face.lightmapTextureMins.y) / (face.lightmapTextureSize.y + 1.0f);
|
||||
corners[i].st.x = (uf::vector::dot( v, texInfo.lightmapVecs[0] ) + 0.5f - face.lightmapTextureMins.x) / (face.lightmapTextureSize.x + 1.0f);
|
||||
corners[i].st.y = (uf::vector::dot( v, texInfo.lightmapVecs[1] ) + 0.5f - face.lightmapTextureMins.y) / (face.lightmapTextureSize.y + 1.0f);
|
||||
}
|
||||
|
||||
int startIndex = 0;
|
||||
@ -283,13 +308,36 @@ namespace impl {
|
||||
pod::Vector2f uvE1 = uf::vector::lerp( corners[3].uv, corners[2].uv, ty );
|
||||
pod::Vector2f stE0 = uf::vector::lerp( corners[0].st, corners[1].st, ty );
|
||||
pod::Vector2f stE1 = uf::vector::lerp( corners[3].st, corners[2].st, ty );
|
||||
/*
|
||||
pod::Vector3f posE0 = uf::vector::lerp( corners[0].pos, corners[1].pos, ty );
|
||||
pod::Vector3f posE1 = uf::vector::lerp( corners[3].pos, corners[2].pos, ty );
|
||||
pod::Vector2f uvE0 = uf::vector::lerp( corners[0].uv, corners[1].uv, ty );
|
||||
pod::Vector2f uvE1 = uf::vector::lerp( corners[3].uv, corners[2].uv, ty );
|
||||
pod::Vector2f stE0 = uf::vector::lerp( corners[0].st, corners[1].st, ty );
|
||||
pod::Vector2f stE1 = uf::vector::lerp( corners[3].st, corners[2].st, ty );
|
||||
*/
|
||||
|
||||
for ( int x = 0; x < side; ++x ) {
|
||||
float tx = (float)x / (side - 1);
|
||||
|
||||
pod::Vector3f basePos = uf::vector::lerp( posE0, posE1, tx );
|
||||
pod::Vector2f finalUv = uf::vector::lerp( uvE0, uvE1, tx );
|
||||
pod::Vector2f finalSt = uf::vector::lerp( stE0, stE1, tx );
|
||||
|
||||
pod::Vector4f vBase = basePos;
|
||||
vBase.w = 1.0f;
|
||||
|
||||
pod::Vector2f finalUv;
|
||||
finalUv.x = uf::vector::dot( vBase, texInfo.textureVecs[0] ) / texWidth;
|
||||
finalUv.y = uf::vector::dot( vBase, texInfo.textureVecs[1] ) / texHeight;
|
||||
|
||||
pod::Vector2f finalSt;
|
||||
finalSt.x = tx;
|
||||
finalSt.y = ty;
|
||||
/*
|
||||
finalSt.x = (uf::vector::dot( vBase, texInfo.lightmapVecs[0] ) + 0.5f - face.lightmapTextureMins.x) / (face.lightmapTextureSize.x + 1.0f);
|
||||
finalSt.y = (uf::vector::dot( vBase, texInfo.lightmapVecs[1] ) + 0.5f - face.lightmapTextureMins.y) / (face.lightmapTextureSize.y + 1.0f);
|
||||
//finalSt.x = 1.0f - finalSt.x; // ?
|
||||
finalSt.y = 1.0f - finalSt.y; // ?
|
||||
*/
|
||||
|
||||
int dispIdx = info.dispVertStart + y * side + x;
|
||||
const auto& dVert = context.dispverts[dispIdx];
|
||||
@ -321,14 +369,24 @@ namespace impl {
|
||||
}
|
||||
}
|
||||
|
||||
auto isVertAllowed = [&](int x, int y) -> bool {
|
||||
int index = y * side + x;
|
||||
int arrayIndex = index / 32;
|
||||
int bitIndex = index % 32;
|
||||
return ( info.allowedVerts[arrayIndex] & (1 << bitIndex) ) != 0;
|
||||
};
|
||||
|
||||
for ( int y = 0; y < side - 1; ++y ) {
|
||||
for ( int x = 0; x < side - 1; ++x ) {
|
||||
if ( !isVertAllowed(x, y) || !isVertAllowed(x + 1, y) || !isVertAllowed(x, y + 1) || !isVertAllowed(x + 1, y + 1) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t v0 = startVertexID + y * side + x;
|
||||
uint32_t v1 = startVertexID + y * side + (x + 1);
|
||||
uint32_t v2 = startVertexID + (y + 1) * side + x;
|
||||
uint32_t v3 = startVertexID + (y + 1) * side + (x + 1);
|
||||
|
||||
// might need to flip winding order
|
||||
meshlet.indices.emplace_back(v0); meshlet.indices.emplace_back(v2); meshlet.indices.emplace_back(v1);
|
||||
meshlet.indices.emplace_back(v1); meshlet.indices.emplace_back(v2); meshlet.indices.emplace_back(v3);
|
||||
}
|
||||
@ -418,6 +476,7 @@ namespace impl {
|
||||
auto classname = metadata["classname"].as<uf::stl::string>("");
|
||||
//UF_MSG_INFO("Entity found: {}", classname);
|
||||
node.name = classname;
|
||||
node.mesh = -1;
|
||||
|
||||
// parse origin
|
||||
auto origin = metadata["origin"].as<uf::stl::string>("");
|
||||
@ -445,8 +504,9 @@ namespace impl {
|
||||
} else if ( model.length() > 4 && model.ends_with(".mdl") ) {
|
||||
auto it = std::find(graph.meshes.begin(), graph.meshes.end(), model);
|
||||
if ( it == graph.meshes.end() ) {
|
||||
auto meshID = graph.meshes.size();
|
||||
if ( ext::valve::loadMdl(graph, model) ) {
|
||||
node.mesh = (int32_t)(graph.meshes.size() - 1);
|
||||
node.mesh = meshID;
|
||||
}
|
||||
} else {
|
||||
node.mesh = (int32_t)std::distance(graph.meshes.begin(), it);
|
||||
@ -554,7 +614,7 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
|
||||
const impl::BspHeader* header = (const impl::BspHeader*)(buffer.data());
|
||||
if ( header->magic != 0x50534256 ) {
|
||||
UF_MSG_ERROR("Invalid VBSP magic number: {}", filename);
|
||||
UF_MSG_ERROR("Invalid VBSP magic number: {:X}", header->magic);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -856,6 +916,8 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
image.loadFromBuffer( missing_pixels, { 2, 2 }, 8, 4 );
|
||||
}
|
||||
|
||||
// disable exporting if loaded from a VPK
|
||||
if ( filename.starts_with("valve://") ) graph.metadata["exporter"]["enabled"] = false;
|
||||
graph.metadata["exporter"]["unwrap"] = false; // not necessary to unwrap
|
||||
|
||||
uf::graph::postprocess( graph );
|
||||
|
||||
@ -6,6 +6,41 @@
|
||||
|
||||
namespace impl {
|
||||
#pragma pack(push, 1)
|
||||
struct mstudiomesh_t {
|
||||
int32_t material;
|
||||
int32_t modelindex;
|
||||
int32_t numvertices;
|
||||
int32_t vertexoffset;
|
||||
int32_t flexes;
|
||||
int32_t flexindex;
|
||||
int32_t materialtype;
|
||||
int32_t materialparam;
|
||||
int32_t meshid;
|
||||
pod::Vector3f center;
|
||||
int32_t vertexdata_ptr;
|
||||
int32_t vertexdata_lod[8];
|
||||
uint8_t pad[32];
|
||||
};
|
||||
|
||||
struct mstudiomodel_t {
|
||||
char name[64];
|
||||
int32_t type;
|
||||
float boundingradius;
|
||||
int32_t nummeshes;
|
||||
int32_t meshindex;
|
||||
int32_t numvertices;
|
||||
int32_t vertexindex;
|
||||
int32_t tangentsindex;
|
||||
uint8_t pad[56];
|
||||
};
|
||||
|
||||
struct mstudiobodyparts_t {
|
||||
int32_t sznameindex;
|
||||
int32_t nummodels;
|
||||
int32_t base;
|
||||
int32_t modelindex;
|
||||
};
|
||||
|
||||
struct vertexFileHeader_t {
|
||||
int32_t magic;
|
||||
int32_t version;
|
||||
@ -18,6 +53,12 @@ namespace impl {
|
||||
int32_t tangentDataStart;
|
||||
};
|
||||
|
||||
struct vertexFileFixup_t {
|
||||
int32_t lod;
|
||||
int32_t sourceVertexID;
|
||||
int32_t numVertexes;
|
||||
};
|
||||
|
||||
struct mstudioboneweight_t {
|
||||
float weight[3];
|
||||
int8_t bone[3];
|
||||
@ -68,8 +109,7 @@ namespace impl {
|
||||
|
||||
// etc
|
||||
};
|
||||
#pragma pack(pop)
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct vtxVertex_t {
|
||||
uint8_t boneWeightIndex[3];
|
||||
uint8_t numBones;
|
||||
@ -167,38 +207,53 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
}
|
||||
|
||||
// extract material directories (cdtextures)
|
||||
uf::stl::vector<uf::stl::string> cdmaterials(mdlHdr->numcdtextures);
|
||||
for ( int i = 0; i < mdlHdr->numcdtextures; ++i ) {
|
||||
int32_t cdOffset = *(int32_t*)(mdlBuffer.data() + mdlHdr->cdtextureindex + (i * 4));
|
||||
uf::stl::string cdPath = (const char*)(mdlBuffer.data() + cdOffset);
|
||||
uf::stl::vector<uf::stl::string> cdmaterials(mdlHdr->numcdtextures);
|
||||
for ( int i = 0; i < mdlHdr->numcdtextures; ++i ) {
|
||||
int32_t cdOffset = *(int32_t*)(mdlBuffer.data() + mdlHdr->cdtextureindex + (i * 4));
|
||||
uf::stl::string cdPath = (const char*)(mdlBuffer.data() + cdOffset);
|
||||
|
||||
std::replace(cdPath.begin(), cdPath.end(), '\\', '/');
|
||||
std::transform(cdPath.begin(), cdPath.end(), cdPath.begin(), ::tolower);
|
||||
cdmaterials[i] = cdPath;
|
||||
}
|
||||
std::replace(cdPath.begin(), cdPath.end(), '\\', '/');
|
||||
std::transform(cdPath.begin(), cdPath.end(), cdPath.begin(), ::tolower);
|
||||
cdmaterials[i] = cdPath;
|
||||
}
|
||||
|
||||
// extract material names from MDL and resolve their full relative paths
|
||||
uf::stl::vector<uf::stl::string> materials(mdlHdr->numtextures);
|
||||
for ( int i = 0; i < mdlHdr->numtextures; ++i ) {
|
||||
int32_t texStructOffset = mdlHdr->textureindex + (i * 64);
|
||||
int32_t nameOffset = *(int32_t*)(mdlBuffer.data() + texStructOffset);
|
||||
uf::stl::string baseName = (const char*)(mdlBuffer.data() + texStructOffset + nameOffset);
|
||||
std::transform(baseName.begin(), baseName.end(), baseName.begin(), ::tolower);
|
||||
// extract material names from MDL and resolve their full relative paths
|
||||
uf::stl::vector<uf::stl::string> materials(mdlHdr->numtextures);
|
||||
for ( int i = 0; i < mdlHdr->numtextures; ++i ) {
|
||||
int32_t texStructOffset = mdlHdr->textureindex + (i * 64);
|
||||
int32_t nameOffset = *(int32_t*)(mdlBuffer.data() + texStructOffset);
|
||||
uf::stl::string baseName = (const char*)(mdlBuffer.data() + texStructOffset + nameOffset);
|
||||
std::transform(baseName.begin(), baseName.end(), baseName.begin(), ::tolower);
|
||||
|
||||
materials[i] = baseName;
|
||||
materials[i] = baseName;
|
||||
|
||||
for ( const auto& cd : cdmaterials ) {
|
||||
uf::stl::string attempt = cd + baseName;
|
||||
if ( uf::vfs::exists("materials/" + attempt + ".vmt") ) {
|
||||
materials[i] = attempt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( const auto& cd : cdmaterials ) {
|
||||
uf::stl::string attempt = cd + baseName;
|
||||
if ( uf::vfs::exists("materials/" + attempt + ".vmt") ) {
|
||||
materials[i] = attempt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// extract LOD0 ertices from VVD
|
||||
// extract LOD0 vertices from VVD
|
||||
const impl::mstudiovertex_t* vvdVertices = (const impl::mstudiovertex_t*)(vvdBuffer.data() + vvdHdr->vertexDataStart);
|
||||
int numLOD0Verts = vvdHdr->numLODVertexes[0];
|
||||
uf::stl::vector<impl::mstudiovertex_t> lod0Vertices;
|
||||
if ( vvdHdr->numFixups == 0 ) {
|
||||
lod0Vertices.assign(vvdVertices, vvdVertices + vvdHdr->numLODVertexes[0]);
|
||||
} else {
|
||||
lod0Vertices.resize(vvdHdr->numLODVertexes[0]);
|
||||
const impl::vertexFileFixup_t* fixups = (const impl::vertexFileFixup_t*)(vvdBuffer.data() + vvdHdr->fixupTableStart);
|
||||
int targetIndex = 0;
|
||||
for ( int i = 0; i < vvdHdr->numFixups; ++i ) {
|
||||
if ( fixups[i].lod >= 0 ) {
|
||||
std::memcpy(&lod0Vertices[targetIndex], &vvdVertices[fixups[i].sourceVertexID], fixups[i].numVertexes * sizeof(impl::mstudiovertex_t));
|
||||
targetIndex += fixups[i].numVertexes;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
*/
|
||||
|
||||
// read VTX file
|
||||
uf::stl::string vtxPath = filename.substr(0, filename.find_last_of('.')) + ".dx90.vtx";
|
||||
@ -213,18 +268,22 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
|
||||
// traverse: BodyPart -> Model -> LOD0 -> Mesh -> StripGroup -> Indices
|
||||
const impl::vtxBodyPart_t* bodyParts = (const impl::vtxBodyPart_t*)(vtxBuffer.data() + vtxHdr->bodyPartOffset);
|
||||
const impl::mstudiobodyparts_t* mdlBodyParts = (const impl::mstudiobodyparts_t*)((uint8_t*)mdlHdr + mdlHdr->bodypartindex);
|
||||
|
||||
for ( int bp = 0; bp < vtxHdr->numBodyParts; ++bp ) {
|
||||
const impl::vtxModel_t* models = (const impl::vtxModel_t*)((uint8_t*)&bodyParts[bp] + bodyParts[bp].modelOffset);
|
||||
const impl::mstudiomodel_t* mdlModels = (const impl::mstudiomodel_t*)((uint8_t*)&mdlBodyParts[bp] + mdlBodyParts[bp].modelindex);
|
||||
|
||||
for ( int m = 0; m < bodyParts[bp].numModels; ++m ) {
|
||||
const impl::vtxModelLOD_t* lods = (const impl::vtxModelLOD_t*)((uint8_t*)&models[m] + models[m].lodOffset);
|
||||
const impl::vtxModelLOD_t& lod0 = lods[0];
|
||||
|
||||
const impl::vtxMesh_t* meshes = (const impl::vtxMesh_t*)((uint8_t*)&lod0 + lod0.meshOffset);
|
||||
const impl::mstudiomesh_t* mdlMeshes = (const impl::mstudiomesh_t*)((uint8_t*)&mdlModels[m] + mdlModels[m].meshindex);
|
||||
|
||||
for ( int meshID = 0; meshID < lod0.numMeshes; ++meshID ) {
|
||||
const impl::vtxMesh_t& mesh = meshes[meshID];
|
||||
const impl::mstudiomesh_t& mdlMesh = mdlMeshes[meshID];
|
||||
|
||||
auto& meshlet = meshlets.emplace_back();
|
||||
uf::stl::unordered_map<uint16_t, uint32_t> vertRemap;
|
||||
@ -245,7 +304,7 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
vertRemap[originalVvdID] = meshlet.vertices.size();
|
||||
auto& vert = meshlet.vertices.emplace_back();
|
||||
|
||||
const auto& srcVert = vvdVertices[originalVvdID];
|
||||
const auto& srcVert = lod0Vertices[mdlMesh.vertexoffset + originalVvdID];
|
||||
|
||||
vert.position = impl::convertPos( srcVert.m_vecPosition );
|
||||
vert.normal = uf::vector::normalize( impl::convertPos( srcVert.m_vecNormal, 1.0f ) );
|
||||
@ -277,16 +336,13 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
|
||||
size_t materialID = 0;
|
||||
uf::stl::string matName = "missing_texture";
|
||||
if ( meshID < materials.size() ) matName = materials[meshID];
|
||||
if ( storage.materials.map.count(matName) > 0 ) {
|
||||
// to-do: add an indexOf
|
||||
for ( ; materialID < graph.materials.size(); ++materialID ) {
|
||||
if ( graph.materials[materialID] == matName ) break;
|
||||
}
|
||||
|
||||
} else {
|
||||
if ( mdlMesh.material < materials.size() ) matName = materials[mdlMesh.material];
|
||||
auto it = std::find(graph.materials.begin(), graph.materials.end(), matName);
|
||||
if ( it == graph.materials.end() ) {
|
||||
materialID = graph.materials.size();
|
||||
auto& material = impl::addMaterial( graph, matName );
|
||||
} else {
|
||||
materialID = (int32_t)std::distance(graph.materials.begin(), it);
|
||||
}
|
||||
|
||||
meshlet.primitive.instance.materialID = materialID;
|
||||
@ -294,16 +350,19 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
}
|
||||
}
|
||||
|
||||
if ( !meshlets.empty() ) {
|
||||
auto meshName = filename;
|
||||
graph.meshes.emplace_back(meshName);
|
||||
graph.primitives.emplace_back(meshName);
|
||||
|
||||
auto& mesh = storage.meshes[meshName];
|
||||
auto& primitives = storage.primitives[meshName];
|
||||
|
||||
mesh.compile( meshlets, primitives );
|
||||
if ( meshlets.empty() ) {
|
||||
UF_MSG_DEBUG("Empty mesh={}", filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto meshName = filename;
|
||||
graph.meshes.emplace_back(meshName);
|
||||
graph.primitives.emplace_back(meshName);
|
||||
|
||||
auto& mesh = storage.meshes[meshName];
|
||||
auto& primitives = storage.primitives[meshName];
|
||||
|
||||
mesh.compile( meshlets, primitives );
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -9,6 +9,9 @@ void impl::solvePositions( uf::stl::vector<pod::Manifold>& manifolds, float dt,
|
||||
for ( auto& manifold : manifolds ) {
|
||||
auto& a = *manifold.a;
|
||||
auto& b = *manifold.b;
|
||||
|
||||
if ( (a.collider.category & pod::Collider::CATEGORY_TRIGGER) || (b.collider.category & pod::Collider::CATEGORY_TRIGGER) ) return;
|
||||
|
||||
auto tA = impl::getTransform( a );
|
||||
auto tB = impl::getTransform( b );
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user