added 3D skybox rendering (to-do: fix some quirks), fixing lighting issues (to-do: fix tangents or normal maps or something i dont know)

This commit is contained in:
ecker 2026-06-20 00:24:33 -05:00
parent aa2740979c
commit def459e72c
17 changed files with 521 additions and 316 deletions

View File

@ -3,7 +3,7 @@
"scenes": {
"start": "StartMenu",
"lights": { "enabled": true,
"lightmaps": false,
"lightmaps": true,
"max": 32,
"shadows": {
"enabled": true,
@ -426,7 +426,7 @@
},
"loader": {
"assert": true,
"async": true
"async": false
},
"hooks": {
"defer lazy calls": true

View File

@ -13,7 +13,7 @@
"physics": { "type": "mesh", "static": true, "mass": 0 },
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
"unwrap mesh": true
},
}
/*"light_environment": { "ignore": false, "light": {
// "color": [0.1, 0.1, 0.1],
"power": 1000,
@ -36,8 +36,7 @@
// "/^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 ] } }
// "/^tools\\/toolsnodraw/": { "material": { "base": [ 1.0, 1.0, 1.0, 0.0 ] } }
}
}
}

View File

@ -0,0 +1,7 @@
{
"import": "./base_sourceengine.json",
"assets": [
{ "filename": "./maps/de_dust2.bsp" }
// { "filename": "./maps/de_dust2/graph.json" }
]
}

View File

@ -1,9 +1,9 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./maps/mcdonalds-mds.bsp" }
{ "filename": "./maps/mcdonalds-mds/graph.json" },
{ "filename": "ent://burger.json", "delay": 1 },
{ "filename": "ent://craeture.json", "delay": 2 }
{ "filename": "./maps/mcdonalds-mds.bsp" }
// { "filename": "./maps/mcdonalds-mds/graph.json" }
// ,{ "filename": "ent://burger.json", "delay": 1 }
// ,{ "filename": "ent://craeture.json", "delay": 2 }
]
}

View File

@ -1,7 +1,8 @@
{
// "import": "./rp_downtown_v2.json"
// "import": "./ss2_medsci1.json"
"import": "./mds_mcdonalds.json"
// "import": "./mds_mcdonalds.json"
// "import": "./cs_office.json"
"import": "./de_dust2.json"
// "import": "./gm_construct.json"
}

View File

@ -322,21 +322,21 @@ void populateSurfaceMaterial() {
if ( 0 <= cubemapIndex && surface.material.roughness < 1.0 ) {
const Texture texture = textures[cubemapIndex];
vec3 V = normalize(surface.position.eye);
vec3 N = surface.normal.eye;
vec3 R = reflect(V, N);
vec3 V = normalize(surface.position.eye);
vec3 N = surface.normal.eye;
vec3 R = reflect(V, N);
mat3 invView = mat3(ubo.eyes[surface.pass].iView);
vec3 worldR = invView * R;
mat3 invView = mat3(ubo.eyes[surface.pass].iView);
vec3 worldR = invView * R;
float mipLevel = surface.material.roughness * mipLevels(textureSize(samplerCubemaps[nonuniformEXT(texture.index)], 0).xy);
vec3 reflection = textureLod(samplerCubemaps[nonuniformEXT(texture.index)], worldR, mipLevel).rgb;
float mipLevel = surface.material.roughness * mipLevels(textureSize(samplerCubemaps[nonuniformEXT(texture.index)], 0).xy);
vec3 reflection = textureLod(samplerCubemaps[nonuniformEXT(texture.index)], worldR, mipLevel).rgb;
vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic);
float cosTheta = max(dot(N, -V), 0.0);
vec3 F = F0 + (max(vec3(1.0 - surface.material.roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
float cosTheta = max(dot(N, -V), 0.0);
vec3 F = F0 + (max(vec3(1.0 - surface.material.roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
surface.light.rgb += reflection * F * surface.material.occlusion;
surface.light.rgb += reflection * F * surface.material.occlusion;
}
// (Occlusion/)Metallic/Roughness map
if ( validTextureIndex( material.indexMetallicRoughness ) ) {
@ -402,6 +402,8 @@ uvec4 uvec2_16x4( uvec2 i ) {
void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
Triangle triangle;
Vertex points[3];
float uvHandedness = 1.0;
if ( isValidAddress(addresses.position) ) {
VPos buf = VPos(nonuniformEXT(addresses.position));
#pragma unroll 3
@ -432,9 +434,12 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
vec2 deltaUV1 = points[1].uv - points[0].uv;
vec2 deltaUV2 = points[2].uv - points[0].uv;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
float det = (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
float r = 1.0f / det;
vec3 tangent_tri = (edge1 * deltaUV2.y - edge2 * deltaUV1.y) * r;
uvHandedness = (det < 0.0) ? -1.0 : 1.0;
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = tangent_tri;
}
@ -489,7 +494,7 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
surface.tangent.world = normalize(vec3( surface.object.model * vec4(triangle.point.tangent, 0.0) ));
surface.tangent.world = normalize(surface.tangent.world - dot(surface.tangent.world, surface.normal.world) * surface.normal.world);
vec3 bitangent = normalize(vec3( surface.object.model * vec4(cross( triangle.point.normal, triangle.point.tangent ), 0.0) ));
vec3 bitangent = normalize(vec3( surface.object.model * vec4(cross( triangle.point.normal, triangle.point.tangent ) * uvHandedness, 0.0) ));
surface.tbn = mat3(surface.tangent.world, bitangent, surface.normal.world);
}
// bind UVs

View File

@ -198,7 +198,7 @@ void populateSurface() {
surface.material.roughness = 0;
surface.material.occlusion = 0;
float depth = 0.0;
float depth = IMAGE_LOAD(samplerDepth).r;
{
#if USE_CAMERA_VIEWPORT
@ -218,10 +218,8 @@ void populateSurface() {
surface.ray.direction = normalize( far3 - near3 );
surface.ray.origin = ubo.eyes[surface.pass].eyePos.xyz; // near3.xyz; // eyePos.xyz
depth = IMAGE_LOAD(samplerDepth).r;
vec4 eye = iProjection * vec4(surface.fragCoord, depth, 1.0);
eye /= eye.w;
if ( eye.w > 0.0001 ) eye /= eye.w;
surface.position.eye = eye.xyz;
surface.position.world = vec3( iView * eye );
@ -234,7 +232,7 @@ void populateSurface() {
const uvec2 ID = msaa.IDs[msaa.currentID];
#endif
if ( ID.x == 0 || ID.y == 0 || depth <= 0.0 ) {
if ( ID.x == 0 || ID.y == 0 /*|| depth <= 0.0*/ ) {
USE_SKYBOX_ON_DIVERGENCE = true;
}
/*

View File

@ -10,7 +10,7 @@ layout (location = 1) in vec2 inUv;
layout (location = 2) in vec4 inColor;
layout (location = 3) in vec2 inSt;
layout (location = 4) in vec3 inNormal;
layout (location = 5) in vec4 inTangent;
layout (location = 5) in vec3 inTangent;
#if SKINNED
layout (location = 6) in uvec4 inJoints;
layout (location = 7) in vec4 inWeights;
@ -19,6 +19,7 @@ layout (location = 5) in vec4 inTangent;
layout( push_constant ) uniform PushBlock {
uint pass;
uint draw;
uint aux;
} PushConstant;
layout (binding = 0) uniform Camera {
@ -69,12 +70,15 @@ void main() {
const Object object = objects[instance.objectID];
const uint jointID = instance.jointID;
uint viewportIndex = gl_ViewIndex;
if ( PushConstant.aux == 1 ) viewportIndex += 2;
#if BAKING
const mat4 view = mat4(1);
const mat4 projection = mat4(1);
#else
const mat4 view = camera.viewport[/*PushConstant.pass*/gl_ViewIndex].view;
const mat4 projection = camera.viewport[/*PushConstant.pass*/gl_ViewIndex].projection;
const mat4 view = camera.viewport[viewportIndex].view;
const mat4 projection = camera.viewport[viewportIndex].projection;
#endif
#if SKINNED
const mat4 skinned = joints.length() <= 0 || jointID < 0 ? mat4(1.0) : inWeights.x * joints[jointID + int(inJoints.x)] + inWeights.y * joints[jointID + int(inJoints.y)] + inWeights.z * joints[jointID + int(inJoints.z)] + inWeights.w * joints[jointID + int(inJoints.w)];

View File

@ -98,19 +98,17 @@ uf::stl::vector<T> uf::stl::KeyMap<T,Key>::flattenByIndex() const {
template<typename T, typename Key>
void uf::stl::KeyMap<T,Key>::merge( KeyMap<T, Key>&& other ) {
this->reserve( this->keys.size() + other.keys.size() );
this->reserve( this->keys.size() + other.keys.size() );
for ( auto& key : other.keys ) {
// Bypass custom operator[] and force a direct map assignment
if ( this->map.count(key) == 0 ) {
this->indices[key] = this->keys.size();
this->keys.emplace_back(key);
}
// Explicitly copy to prevent any weird std::move state corruption
this->map[key] = other.map.at(key);
}
for ( auto& key : other.keys ) {
if ( this->map.count(key) == 0 ) {
this->indices[key] = this->keys.size();
this->keys.emplace_back(key);
}
this->map[key] = other.map.at(key);
}
other.clear();
other.clear();
}
template<typename T, typename Key>

View File

@ -282,8 +282,8 @@ void ext::ExtSceneBehavior::initialize( uf::Object& self ) {
#endif
}
void ext::ExtSceneBehavior::tick( uf::Object& self ) {
// auto& assetLoader = this->getComponent<uf::asset>();
uf::asset::processQueue();
// auto& assetLoader = this->getComponent<uf::asset>();
auto& metadata = this->getComponent<ext::ExtSceneBehavior::Metadata>();
auto& metadataJson = this->getComponent<uf::Serializer>();
@ -625,8 +625,9 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) {
constexpr uint32_t MODE_CUBEMAP = 1;
constexpr uint32_t MODE_SEPARATE_2DS = 2;
for ( uint32_t i = 0; i < entities.size(); ++i ) {
auto& info = entities[i];
//for ( uint32_t i = 0; i < entities.size(); ++i ) {
for ( auto idx : indices ) {
auto& info = entities[idx];
uf::Entity* entity = info.entity;
int32_t boundIndexMap = -1;

View File

@ -710,12 +710,20 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M
graphic.initialize();
graphic.initializeMesh( mesh );
graphic.device = &uf::renderer::device;
graphic.material.device = &uf::renderer::device;
graphic.descriptor.frontFace = graphMetadataJson["renderer"]["invert"].as<bool>(true) ? uf::renderer::enums::Face::CW : uf::renderer::enums::Face::CCW;
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::BACK;
auto& entityName = entity.getName();
auto& metadataJson = entity.getComponent<uf::Serializer>();
auto& metadataValve = metadataJson["valve"];
if ( entityName == "skybox" || metadataValve["skyboxed"].as<bool>(false) ) {
graphic.descriptor.aux = 1; // to-do: some global enums for this shit
}
auto tag = ext::json::find( entity.getName(), graphMetadataJson["tags"] );
auto tag = ext::json::find( entityName, graphMetadataJson["tags"] );
if ( !ext::json::isObject( tag ) ) {
tag["renderer"] = graphMetadataJson["renderer"];
}
@ -731,7 +739,7 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M
}
// query materials if culling needs to be disabled
if ( entity.getName() != "worldspawn" ) {
if ( entityName != "worldspawn" ) {
for ( auto& primitive : primitives ) {
auto materialID = primitive.instance.materialID;
if ( 0 <= materialID && materialID <= graph.materials.size() ) {
@ -1019,6 +1027,9 @@ void uf::graph::process( pod::Graph& graph ) {
// nodraw
if ( name.starts_with("tools/") ) {
material.colorBase.w = 0.0f;
material.modeAlpha = pod::Material::AlphaMode::MASK;
material.factorAlphaCutoff = 1.0f;
material.indexAlbedo = -1;
}
}

View File

@ -67,12 +67,6 @@ const uf::Entity& uf::Scene::getController() const {
}
uf::Camera& uf::Scene::getCamera( uf::Entity& controller ) {
// ???
/*
if ( auto currentRenderMode = uf::renderer::getCurrentRenderMode(); currentRenderMode && !currentRenderMode->getName().empty() ) {
return controller.getComponent<uf::Camera>();
}
*/
#if !UF_SCENE_GLOBAL_GRAPH
auto& metadata = this->getComponent<uf::SceneBehavior::Metadata>();
#endif
@ -83,15 +77,39 @@ uf::Camera& uf::Scene::getCamera( uf::Entity& controller ) {
if ( lastFrame != uf::time::frame ) {
auto& sourceCamera = controller.getComponent<uf::Camera>();
if ( controller.getName() == "Player" ) sourceCamera.update();
auto& controllerName = controller.getName();
if ( controllerName == "Player" ) sourceCamera.update();
// copy all matrices
for ( auto i = 0; i < uf::camera::maxViews; ++i ) {
cachedCamera.setView(sourceCamera.getView(i), i);
cachedCamera.setProjection(sourceCamera.getProjection(i), i);
}
// flatten the transform in the event the parent transform updates later
cachedCamera.setTransform(uf::transform::flatten(sourceCamera.getTransform()));
auto transform = uf::transform::flatten(sourceCamera.getTransform());
cachedCamera.setTransform(transform);
lastFrame = uf::time::frame;
// handle sky_camera
if ( auto entity = this->findByName("sky_camera"); entity && controllerName == "Player" ) {
auto& metadatavalve = entity->getComponent<uf::Serializer>()["valve"];
float scale = metadatavalve["scale"].as<float>(16.0f);
auto cameraTransform = entity->getComponent<pod::Transform<>>();
cameraTransform.position += (transform.position / scale);
cameraTransform.orientation = transform.orientation;
uf::Camera skyCamera = sourceCamera;
skyCamera.setTransform(cameraTransform);
skyCamera.update();
int auxOffset = 2;
for ( auto i = 0; i < 2; ++i ) {
if ( (i + auxOffset) < uf::camera::maxViews ) {
cachedCamera.setView(skyCamera.getView(i), i + auxOffset);
cachedCamera.setProjection(sourceCamera.getProjection(i), i + auxOffset);
}
}
}
}
return cachedCamera;
}

View File

@ -6,6 +6,7 @@
#include <uf/ext/zlib/zlib.h>
#include <uf/utils/mesh/grid.h>
#include <uf/utils/memory/unordered_set.h>
namespace impl {
struct RGBE {
@ -121,6 +122,31 @@ namespace impl {
#pragma pack(push, 1)
struct BspPlane {
pod::Vector3f normal;
float dist;
int32_t type;
};
struct BspNode {
int32_t planenum;
int32_t children[2];
int16_t mins[3], maxs[3];
uint16_t firstface, numfaces;
int16_t area;
int16_t padding;
};
struct BspLeaf {
int32_t contents;
int16_t cluster;
int16_t area_flags;
int16_t mins[3], maxs[3];
uint16_t firstleafface, numleaffaces;
uint16_t firstleafbrush, numleafbrushes;
int16_t leafWaterDataID;
};
struct BspDispSubNeighbor {
uint16_t neighbor;
uint8_t neighborOrientation;
@ -205,14 +231,17 @@ namespace impl {
uf::stl::vector<int32_t> surfedges;
uf::stl::vector<impl::BspFace> faces;
// brush, brushside
// node, leaf
// leafface, leaffbrush
uf::stl::vector<impl::BspPlane> planes;
uf::stl::vector<impl::BspNode> nodes;
uf::stl::vector<impl::BspLeaf> leafs;
uf::stl::vector<uint16_t> leaffaces;
// leaffbrush
uf::stl::vector<impl::BspTexInfo> texinfos;
uf::stl::vector<impl::BspTexData> texdatas;
uf::stl::vector<int32_t> stringTable;
uf::stl::string stringData;
uf::stl::vector<impl::BspModel> models;
// visibility
uf::stl::vector<uint8_t> visibility;
uf::stl::vector<int8_t> entities;
uf::stl::vector<impl::BspGameLump> gameLumps;
uf::stl::vector<impl::BspDispInfo> dispinfos;
@ -228,6 +257,8 @@ namespace impl {
// worldlight
// other
int32_t skyArea = -1;
uf::stl::unordered_set<int32_t> skyboxFaces;
uf::stl::vector<int32_t> modelToMesh;
uf::stl::vector<int32_t> texdataToMaterial;
uf::stl::vector<int32_t> cubemapIDs;
@ -258,6 +289,49 @@ namespace impl {
return io;
}
int32_t findLeaf( const impl::BspContext& context, const pod::Vector3f& pos ) {
if ( context.nodes.empty() || context.planes.empty() || context.leafs.empty() ) return 0;
int32_t node = 0;
while ( node >= 0 ) {
const auto& n = context.nodes[node];
const auto& p = context.planes[n.planenum];
float d = uf::vector::dot(pos, p.normal) - p.dist;
node = n.children[d < 0 ? 1 : 0];
}
return -node - 1;
}
bool isClusterVisible( const impl::BspContext& context, int32_t visCluster, int32_t testCluster ) {
if ( context.visibility.size() < 8 || visCluster < 0 || testCluster < 0 ) return true;
// PVS Header: [num_clusters] [byteofs_pvs[num_clusters]] [byteofs_phs[num_clusters]]
int32_t numClusters = *(int32_t*)(context.visibility.data());
if ( visCluster >= numClusters || testCluster >= numClusters ) return false;
// offset to this cluster's PVS data
int32_t byteOffset = *(int32_t*)(context.visibility.data() + 4 + visCluster * 8);
const uint8_t* pvs = context.visibility.data() + byteOffset;
// decompress RLE
int32_t clusterCounter = 0;
while ( clusterCounter <= testCluster ) {
if ( *pvs == 0 ) {
clusterCounter += 8 * pvs[1];
pvs += 2;
} else {
if ( clusterCounter + 8 > testCluster ) {
return (*pvs & (1 << (testCluster - clusterCounter))) != 0;
}
clusterCounter += 8;
pvs++;
}
}
return false;
}
int32_t findClosestCubemap( const impl::BspContext& context, const pod::Vector3f& position ) {
if ( context.cubemapIDs.empty() ) return -1;
@ -392,10 +466,13 @@ namespace impl {
pod::Vector3f tangent = uf::vector::normalize(pR - pL);
pod::Vector3f bitangent = uf::vector::normalize(pU - pD);
pod::Vector3f normal = uf::vector::normalize(uf::vector::cross(bitangent, tangent));
// float w = (uf::vector::dot(uf::vector::cross(normal, tangent), bitangent) < 0.0f) ? -1.0f : 1.0f;
meshlet.vertices[id].normal = normal;
meshlet.vertices[id].tangent = tangent;
meshlet.vertices[id].tangent = tangent = uf::vector::normalize(tangent - normal * uf::vector::dot(normal, tangent));
}
}
@ -435,7 +512,7 @@ namespace impl {
const auto& face = context.faces[faceID];
pod::Vector4f vertex = context.vertices[vertexID];
vertex.w = 1; // for dot products
// add index
meshlet.indices.emplace_back((uint32_t)(meshlet.vertices.size()));
// add vertex
@ -443,7 +520,7 @@ namespace impl {
v.position = pos;
v.color = { 1.0f, 1.0f, 1.0f, 1.0f };
v.normal = normal;
// has texture information
if ( face.texinfo >= 0 && face.texinfo < context.texinfos.size() ) {
const auto& info = context.texinfos[face.texinfo];
@ -459,7 +536,11 @@ namespace impl {
v.st = uf::atlas::mapUv( context.lightmapAtlas, v.st, impl::faceHash( faceID ) );
v.tangent = uf::vector::normalize( impl::convertPos( info.textureVecs[0], 1) );
pod::Vector3f t = uf::vector::normalize( impl::convertPos( info.textureVecs[0], 1.0f ) );
pod::Vector3f b = uf::vector::normalize( impl::convertPos( info.textureVecs[1], 1.0f ) );
v.tangent = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
// float w = (uf::vector::dot( uf::vector::cross(normal, t), b ) < 0.0f) ? -1.0f : 1.0f;
}
};
@ -478,6 +559,21 @@ namespace impl {
return data;
}
template<>
void extractLump( const uf::stl::vector<uint8_t>& buffer, const impl::BspLump& lump, uf::stl::vector<impl::BspLeaf>& data ) {
if ( lump.length == 0 || lump.offset >= buffer.size() ) return;
int stride = (lump.version == 0) ? 56 : 32;
size_t count = lump.length / stride;
data.resize(count);
const uint8_t* src = buffer.data() + lump.offset;
for ( size_t i = 0; i < count; ++i ) {
std::memcpy(&data[i], src + (i * stride), sizeof(impl::BspLeaf));
}
}
template<>
void extractLump( const uf::stl::vector<uint8_t>& buffer, const impl::BspLump& lump, uf::stl::vector<impl::BspGameLump>& data ) {
if ( lump.length == 0 || lump.offset >= buffer.size() ) return;
@ -516,6 +612,9 @@ namespace impl {
for ( auto nodeID : graph.root.children ) {
auto& node = graph.nodes[nodeID];
auto& metadata = node.metadata["valve"];
if ( !ext::json::isObject( metadata ) ) continue;
auto classname = metadata["classname"].as<uf::stl::string>("");
//UF_MSG_INFO("Entity found: {}", classname);
node.name = classname;
@ -691,11 +790,16 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
impl::extractLump<impl::BspEdge>(buffer, header->lumps[impl::BspLump::LUMP_EDGES], context.edges);
impl::extractLump<int32_t>(buffer, header->lumps[impl::BspLump::LUMP_SURFEDGES], context.surfedges);
impl::extractLump<impl::BspFace>(buffer, header->lumps[impl::BspLump::LUMP_FACES], context.faces);
impl::extractLump<impl::BspPlane>(buffer, header->lumps[impl::BspLump::LUMP_PLANES], context.planes);
impl::extractLump<impl::BspNode>(buffer, header->lumps[impl::BspLump::LUMP_NODES], context.nodes);
impl::extractLump<impl::BspLeaf>(buffer, header->lumps[impl::BspLump::LUMP_LEAFS], context.leafs);
impl::extractLump<uint16_t>(buffer, header->lumps[impl::BspLump::LUMP_LEAFFACES], context.leaffaces);
impl::extractLump<impl::BspTexInfo>(buffer, header->lumps[impl::BspLump::LUMP_TEXINFO], context.texinfos);
impl::extractLump<impl::BspTexData>(buffer, header->lumps[impl::BspLump::LUMP_TEXDATA], context.texdatas);
impl::extractLump<int32_t>(buffer, header->lumps[impl::BspLump::LUMP_TEXDATA_STRING_TABLE], context.stringTable);
impl::extractLumpString(buffer, header->lumps[impl::BspLump::LUMP_TEXDATA_STRING_DATA], context.stringData);
impl::extractLump<impl::BspModel>(buffer, header->lumps[impl::BspLump::LUMP_MODELS], context.models);
impl::extractLump<uint8_t>(buffer, header->lumps[impl::BspLump::LUMP_VISIBILITY], context.visibility);
impl::extractLump<int8_t>(buffer, header->lumps[impl::BspLump::LUMP_ENTITIES], context.entities);
impl::extractLump<impl::BspGameLump>(buffer, header->lumps[impl::BspLump::LUMP_GAME_LUMP], context.gameLumps);
impl::extractLump<impl::BspDispInfo>(buffer, header->lumps[impl::BspLump::LUMP_DISPINFO], context.dispinfos);
@ -730,7 +834,6 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
auto& image = storage.images[matName].data;
auto& texture = storage.images[matName].handle;
if ( ext::valve::loadVtf( image, vtfPath ) ) {
UF_MSG_DEBUG("Loaded cubemap={}", vtfPath);
context.cubemapIDs.emplace_back( storage.materials[matName].indexCubemap = textureID );
}
}
@ -787,106 +890,6 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
storage.textures[textureKey].index = atlasImageID;
}
// read models
for ( auto m = 0; m < context.models.size(); ++m ) {
const auto& model = context.models[m];
uf::stl::unordered_map<int32_t, impl::Meshlet> meshlets; // group by material IDs
for ( auto i = 0; i < model.numfaces; ++i ) {
const auto faceID = model.firstface + i;
const auto& face = context.faces[faceID];
if ( face.numedges < 3 ) continue;
if ( face.texinfo < 0 || face.texinfo >= context.texinfos.size() ) continue;
int32_t texDataID = context.texinfos[face.texinfo].texData;
if ( texDataID < 0 || texDataID >= context.texdatas.size() ) continue;
size_t materialID = context.texdataToMaterial[texDataID];
auto& matName = graph.materials[materialID];
// read brush
auto& meshlet = meshlets[materialID];
meshlet.primitive.instance.materialID = materialID;
if ( 0 <= face.lightofs ) {
meshlet.primitive.instance.lightmapID = atlasTextureID;
}
if ( !context.cubemapIDs.empty() ) {
int32_t pivotSurfEdge = context.surfedges[face.firstedge];
uint16_t pivotVertID = pivotSurfEdge >= 0 ? context.edges[pivotSurfEdge].x : context.edges[-pivotSurfEdge].y;
pod::Vector3f p0 = impl::convertPos( context.vertices[pivotVertID] );
meshlet.primitive.instance.cubemapID = impl::findClosestCubemap( context, p0 );
}
if ( face.dispinfo != -1 ) {
impl::buildDisplacement( context, meshlet, faceID );
continue;
}
const auto edgeID = face.firstedge;
int32_t pivotSurfEdge = context.surfedges[edgeID];
uint16_t pivotVertID = pivotSurfEdge >= 0 ? context.edges[pivotSurfEdge].x : context.edges[-pivotSurfEdge].y;
pod::Vector3f p0 = impl::convertPos( context.vertices[pivotVertID] );
for ( int16_t i = 1; i < face.numedges - 1; ++i ) {
int32_t se1 = context.surfedges[edgeID + i];
int32_t se2 = context.surfedges[edgeID + i + 1];
uint16_t v1 = se1 >= 0 ? context.edges[se1].x : context.edges[-se1].y;
uint16_t v2 = se2 >= 0 ? context.edges[se2].x : context.edges[-se2].y;
pod::Vector3f p1 = impl::convertPos( context.vertices[v1] );
pod::Vector3f p2 = impl::convertPos( context.vertices[v2] );
pod::Vector3f normal = uf::vector::normalize(uf::vector::cross(p1 - p0, p2 - p0));
impl::addVertex( context, meshlet, pivotVertID, p0, normal, faceID );
impl::addVertex( context, meshlet, v1, p1, normal, faceID );
impl::addVertex( context, meshlet, v2, p2, normal, faceID );
}
}
if ( meshlets.empty() ) continue;
auto meshName = ::fmt::format("model_{}", m);
context.modelToMesh[m] = graph.meshes.size();
graph.meshes.emplace_back(meshName);
graph.primitives.emplace_back(meshName);
auto& mesh = storage.meshes[meshName];
auto& primitives = storage.primitives[meshName];
// slice worldspawn
if ( false && m == 0 ) {
/*
if ( ext::json::isObject( meshgrid.metadata ) ) {
if ( meshgrid.metadata["size"].is<size_t>() ) {
size_t d = meshgrid.metadata["size"].as<size_t>();
meshgrid.grid.divisions = {d, d, d};
} else {
meshgrid.grid.divisions = uf::vector::decode( meshgrid.metadata["size"], meshgrid.grid.divisions );
}
meshgrid.eps = meshgrid.metadata["epsilon"].as(meshgrid.eps);
meshgrid.print = meshgrid.metadata["print"].as(meshgrid.print);
meshgrid.clip = meshgrid.metadata["clip"].as(meshgrid.clip);
meshgrid.cleanup = meshgrid.metadata["cleanup"].as(meshgrid.cleanup);
}
*/
uf::meshgrid::Grid grid;
grid.divisions = {8, 8, 8};
auto mlets = uf::stl::values( meshlets );
auto partitioned = uf::meshgrid::partition( grid, mlets, EPS, true, true );
mesh.compile( partitioned, primitives );
} else {
mesh.compile( meshlets, primitives );
}
}
// read entities
{
bool parsing = false;
@ -941,6 +944,186 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
}
}
// prepare for segregating skybox from worldspawn
{
pod::Vector3f skyCameraOrigin = {NAN, NAN, NAN};
for ( auto& node : graph.nodes ) {
auto& metadataValve = node.metadata["valve"];
if ( metadataValve["classname"].as<uf::stl::string>("") != "sky_camera" ) continue;
auto originStr = metadataValve["origin"].as<uf::stl::string>("");
if ( originStr == "" ) continue;
skyCameraOrigin = impl::str2vec<pod::Vector3f>( originStr );
break;
}
if ( uf::vector::isValid( skyCameraOrigin ) ) {
int32_t skyLeafIdx = impl::findLeaf(context, skyCameraOrigin);
context.skyArea = context.leafs[skyLeafIdx].area_flags & 0x01FF;
for ( const auto& leaf : context.leafs ) {
int16_t leafArea = leaf.area_flags & 0x01FF;
if ( leafArea == context.skyArea ) {
for ( int i = 0; i < leaf.numleaffaces; ++i ) {
context.skyboxFaces.insert(context.leaffaces[leaf.firstleafface + i]);
}
}
}
}
}
// read models
for ( auto m = 0; m < context.models.size(); ++m ) {
const auto& model = context.models[m];
uf::stl::unordered_map<int32_t, impl::Meshlet> meshlets; // group by material IDs
uf::stl::unordered_map<int32_t, impl::Meshlet> skyboxMeshlets; // segregate skybox geometry
for ( auto i = 0; i < model.numfaces; ++i ) {
const auto faceID = model.firstface + i;
const auto& face = context.faces[faceID];
if ( face.numedges < 3 ) continue;
if ( face.texinfo < 0 || face.texinfo >= context.texinfos.size() ) continue;
int32_t texDataID = context.texinfos[face.texinfo].texData;
if ( texDataID < 0 || texDataID >= context.texdatas.size() ) continue;
size_t materialID = context.texdataToMaterial[texDataID];
auto& matName = graph.materials[materialID];
// deduce which group to use
bool isSkybox = (m == 0 && context.skyboxFaces.count(faceID) > 0);
if ( !isSkybox && m == 0 && context.skyArea != -1 ) {
int32_t se0 = context.surfedges[face.firstedge];
int32_t se1 = context.surfedges[face.firstedge + 1];
int32_t se2 = context.surfedges[face.firstedge + 2];
uint16_t v0 = se0 >= 0 ? context.edges[se0].x : context.edges[-se0].y;
uint16_t v1 = se1 >= 0 ? context.edges[se1].x : context.edges[-se1].y;
uint16_t v2 = se2 >= 0 ? context.edges[se2].x : context.edges[-se2].y;
pod::Vector3f p0 = context.vertices[v0];
pod::Vector3f p1 = context.vertices[v1];
pod::Vector3f p2 = context.vertices[v2];
pod::Vector3f normal = uf::vector::normalize(uf::vector::cross(p1 - p0, p2 - p0));
pod::Vector3f testPos = p0 + normal * 2.0f;
int32_t testLeafIdx = impl::findLeaf(context, testPos);
if ( testLeafIdx >= 0 && testLeafIdx < context.leafs.size() ) {
int16_t testArea = context.leafs[testLeafIdx].area_flags & 0x01FF;
if ( testArea == context.skyArea ) {
isSkybox = true;
}
}
}
// read brush
auto& meshlet = (isSkybox ? skyboxMeshlets : meshlets)[materialID];
meshlet.primitive.instance.materialID = materialID;
if ( 0 <= face.lightofs ) {
meshlet.primitive.instance.lightmapID = atlasTextureID;
}
if ( !context.cubemapIDs.empty() ) {
int32_t pivotSurfEdge = context.surfedges[face.firstedge];
uint16_t pivotVertID = pivotSurfEdge >= 0 ? context.edges[pivotSurfEdge].x : context.edges[-pivotSurfEdge].y;
pod::Vector3f p0 = impl::convertPos( context.vertices[pivotVertID] );
meshlet.primitive.instance.cubemapID = impl::findClosestCubemap( context, p0 );
}
if ( face.dispinfo != -1 ) {
impl::buildDisplacement( context, meshlet, faceID );
continue;
}
const auto edgeID = face.firstedge;
int32_t pivotSurfEdge = context.surfedges[edgeID];
uint16_t pivotVertID = pivotSurfEdge >= 0 ? context.edges[pivotSurfEdge].x : context.edges[-pivotSurfEdge].y;
pod::Vector3f p0 = impl::convertPos( context.vertices[pivotVertID] );
const auto& plane = context.planes[face.planenum];
pod::Vector3f faceNormal = {0.0f, 0.0f, 0.0f};
for ( int16_t i = 1; i < face.numedges - 1; ++i ) {
int32_t se1 = context.surfedges[edgeID + i];
int32_t se2 = context.surfedges[edgeID + i + 1];
uint16_t v1 = se1 >= 0 ? context.edges[se1].x : context.edges[-se1].y;
uint16_t v2 = se2 >= 0 ? context.edges[se2].x : context .edges[-se2].y;
pod::Vector3f p1 = impl::convertPos( context.vertices[v1] );
pod::Vector3f p2 = impl::convertPos( context.vertices[v2] );
faceNormal += uf::vector::cross(p1 - p0, p2 - p0);
}
faceNormal = uf::vector::normalize(faceNormal);
for ( int16_t i = 1; i < face.numedges - 1; ++i ) {
int32_t se1 = context.surfedges[edgeID + i];
int32_t se2 = context.surfedges[edgeID + i + 1];
uint16_t v1 = se1 >= 0 ? context.edges[se1].x : context.edges[-se1].y;
uint16_t v2 = se2 >= 0 ? context.edges[se2].x : context.edges[-se2].y;
pod::Vector3f p1 = impl::convertPos( context.vertices[v1] );
pod::Vector3f p2 = impl::convertPos( context.vertices[v2] );
impl::addVertex( context, meshlet, pivotVertID, p0, faceNormal, faceID );
impl::addVertex( context, meshlet, v1, p1, faceNormal, faceID );
impl::addVertex( context, meshlet, v2, p2, faceNormal, faceID );
}
}
if ( !meshlets.empty() ) {
auto meshName = ::fmt::format("model_{}", m);
context.modelToMesh[m] = graph.meshes.size();
graph.meshes.emplace_back(meshName);
graph.primitives.emplace_back(meshName);
auto& mesh = storage.meshes[meshName];
auto& primitives = storage.primitives[meshName];
// for ( auto& pair : meshlets ) uf::mesh::tangents( pair.second.vertices, pair.second.indices );
// slice worldspawn
if ( false && m == 0 ) {
uf::meshgrid::Grid grid;
grid.divisions = {8, 8, 8};
auto mlets = uf::stl::values( meshlets );
auto partitioned = uf::meshgrid::partition( grid, mlets, EPS, true, true );
mesh.compile( partitioned, primitives );
} else {
mesh.compile( meshlets, primitives );
}
}
if ( !skyboxMeshlets.empty() ) {
auto meshName = ::fmt::format("model_{}_skybox", m);
graph.meshes.emplace_back(meshName);
graph.primitives.emplace_back(meshName);
auto& mesh = storage.meshes[meshName];
auto& primitives = storage.primitives[meshName];
mesh.compile( skyboxMeshlets, primitives );
// create a skybox node for this
auto nodeID = graph.nodes.size();
auto& node = graph.nodes.emplace_back();
node.name = "skybox";
node.mesh = (int32_t)(graph.meshes.size() - 1);
graph.root.children.emplace_back(nodeID);
}
}
// read static props
for ( const auto& item : context.gameLumps ) {
if ( item.id == 1936749168 ) { // 'sprp' (Static Props)
@ -991,6 +1174,16 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
metadata["model"] = dict[type];
metadata["origin"] = ::fmt::format("{} {} {}", origin.x, origin.y, origin.z);
metadata["angles"] = ::fmt::format("{} {} {}", angles.x, angles.y, angles.z);
if ( context.skyArea != -1 ) {
int32_t propLeafIdx = impl::findLeaf(context, origin);
if ( propLeafIdx >= 0 && propLeafIdx < context.leafs.size() ) {
int16_t propArea = context.leafs[propLeafIdx].area_flags & 0x01FF;
if ( propArea == context.skyArea ) {
metadata["skyboxed"] = true;
}
}
}
graph.root.children.emplace_back( nodeID );
}
@ -1004,6 +1197,9 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
// load materials
uf::stl::vector<uint8_t> missing_pixels = { 255, 0, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 0, 255, 255 };
for ( auto matName : graph.materials ) {
if ( matName == "" ) {
continue;
}
uf::Serializer vmt;
auto vmtPath = ::fmt::format("materials/{}.vmt", matName);
auto vtfPath = ::fmt::format("materials/{}.vtf", matName);
@ -1054,13 +1250,15 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
texture.viewType = uf::renderer::enums::Image::VIEW_TYPE_CUBE;
if ( ext::valve::loadVtf( image, vtfPath ) ) {
UF_MSG_DEBUG("Loaded cubemap={}", matName);
material.indexCubemap = textureID;
}
}
}
// bumpmap
if ( vmt["$ssbump"].as<int>(0) == 1 ) {
material.indexNormal = -1;
// normal map
if ( vmt["$bumpmap"].is<uf::stl::string>() ) {
} else if ( vmt["$bumpmap"].is<uf::stl::string>() ) {
auto matName = uf::string::lowercase(vmt["$bumpmap"].as<uf::stl::string>());
auto vtfPath = ::fmt::format("materials/{}.vtf", matName);

View File

@ -320,94 +320,66 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
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];
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;
auto& meshlet = meshlets.emplace_back();
uf::stl::unordered_map<uint16_t, uint32_t> vertRemap;
const impl::vtxStripGroup_t* stripGroups = (const impl::vtxStripGroup_t*)((uint8_t*)&mesh + mesh.stripGroupHeaderOffset);
for ( int sg = 0; sg < mesh.numStripGroups; ++sg ) {
const impl::vtxStripGroup_t& stripGroup = stripGroups[sg];
const impl::vtxStripGroup_t* stripGroups = (const impl::vtxStripGroup_t*)((uint8_t*)&mesh + mesh.stripGroupHeaderOffset);
for ( int sg = 0; sg < mesh.numStripGroups; ++sg ) {
const impl::vtxStripGroup_t& stripGroup = stripGroups[sg];
const uint16_t* indices = (const uint16_t*)((uint8_t*)&stripGroup + stripGroup.indexOffset);
const impl::vtxVertex_t* vtxVerts = (const impl::vtxVertex_t*)((uint8_t*)&stripGroup + stripGroup.vertOffset);
const uint16_t* indices = (const uint16_t*)((uint8_t*)&stripGroup + stripGroup.indexOffset);
const impl::vtxVertex_t* vtxVerts = (const impl::vtxVertex_t*)((uint8_t*)&stripGroup + stripGroup.vertOffset);
for ( int i = 0; i < stripGroup.numIndices; i += 3 ) {
uint32_t tri[3];
for ( int j = 0; j < 3; ++j ) {
uint16_t localVertIndex = indices[i + j];
const impl::vtxVertex_t& vtxVert = vtxVerts[localVertIndex];
uint16_t originalVvdID = vtxVert.origMeshVertID;
for ( int i = 0; i < stripGroup.numIndices; i += 3 ) {
uint32_t tri[3];
for ( int j = 0; j < 3; ++j ) {
uint16_t localVertIndex = indices[i + j];
const impl::vtxVertex_t& vtxVert = vtxVerts[localVertIndex];
uint16_t originalVvdID = vtxVert.origMeshVertID;
if ( vertRemap.find(originalVvdID) == vertRemap.end() ) {
vertRemap[originalVvdID] = meshlet.vertices.size();
auto& vert = meshlet.vertices.emplace_back();
if ( vertRemap.find(originalVvdID) == vertRemap.end() ) {
vertRemap[originalVvdID] = meshlet.vertices.size();
auto& vert = meshlet.vertices.emplace_back();
const auto& srcVert = lod0Vertices[mdlMesh.vertexoffset + 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 ) );
if ( !lod0Tangents.empty() ) {
vert.tangent = uf::vector::normalize( impl::convertPos( lod0Tangents[mdlMesh.vertexoffset + originalVvdID], 1.0f ) );
} else {
vert.tangent = {};
}
vert.position = impl::convertPos( srcVert.m_vecPosition );
vert.normal = uf::vector::normalize( impl::convertPos( srcVert.m_vecNormal, 1.0f ) );
vert.uv = srcVert.m_vecTexCoord;
vert.color = {1.0f, 1.0f, 1.0f, 1.0f};
vert.joints.x = srcVert.m_BoneWeights.numbones > 0 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[0]) : 0;
vert.joints.y = srcVert.m_BoneWeights.numbones > 1 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[1]) : 0;
vert.joints.z = srcVert.m_BoneWeights.numbones > 2 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[2]) : 0;
vert.joints.w = 0;
if ( !lod0Tangents.empty() ) {
vert.tangent = uf::vector::normalize( impl::convertPos( lod0Tangents[mdlMesh.vertexoffset + originalVvdID], 1.0f ) );
}
vert.weights.x = srcVert.m_BoneWeights.numbones > 0 ? srcVert.m_BoneWeights.weight[0] : 1.0f;
vert.weights.y = srcVert.m_BoneWeights.numbones > 1 ? srcVert.m_BoneWeights.weight[1] : 0.0f;
vert.weights.z = srcVert.m_BoneWeights.numbones > 2 ? srcVert.m_BoneWeights.weight[2] : 0.0f;
vert.weights.w = 0.0f;
vert.uv = srcVert.m_vecTexCoord;
vert.color = {1.0f, 1.0f, 1.0f, 1.0f};
vert.joints.x = srcVert.m_BoneWeights.numbones > 0 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[0]) : 0;
vert.joints.y = srcVert.m_BoneWeights.numbones > 1 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[1]) : 0;
vert.joints.z = srcVert.m_BoneWeights.numbones > 2 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[2]) : 0;
vert.joints.w = 0;
// Bounds calculation
auto& bounds = meshlet.primitive.instance.bounds;
if ( vertRemap.size() == 1 ) {
bounds.min = bounds.max = vert.position;
} else {
bounds.min = uf::vector::min( bounds.min, vert.position );
bounds.max = uf::vector::max( bounds.max, vert.position );
}
}
vert.weights.x = srcVert.m_BoneWeights.numbones > 0 ? srcVert.m_BoneWeights.weight[0] : 1.0f;
vert.weights.y = srcVert.m_BoneWeights.numbones > 1 ? srcVert.m_BoneWeights.weight[1] : 0.0f;
vert.weights.z = srcVert.m_BoneWeights.numbones > 2 ? srcVert.m_BoneWeights.weight[2] : 0.0f;
vert.weights.w = 0.0f;
meshlet.indices.push_back(tri[j] = vertRemap[originalVvdID]);
}
auto& bounds = meshlet.primitive.instance.bounds;
if ( vertRemap.size() == 1 ) {
bounds.min = bounds.max = vert.position;
} else {
bounds.min = uf::vector::min( bounds.min, vert.position );
bounds.max = uf::vector::max( bounds.max, vert.position );
}
}
if ( lod0Tangents.empty() ) {
auto& v0 = meshlet.vertices[tri[0]];
auto& v1 = meshlet.vertices[tri[1]];
auto& v2 = meshlet.vertices[tri[2]];
meshlet.indices.push_back(tri[j] = vertRemap[originalVvdID]);
}
}
}
pod::Vector3f edge1 = v1.position - v0.position;
pod::Vector3f edge2 = v2.position - v0.position;
pod::Vector2f duv1 = v1.uv - v0.uv;
pod::Vector2f duv2 = v2.uv - v0.uv;
float f = 1.0f / (duv1.x * duv2.y - duv2.x * duv1.y);
pod::Vector3f tangent = {
f * (duv2.y * edge1.x - duv1.y * edge2.x),
f * (duv2.y * edge1.y - duv1.y * edge2.y),
f * (duv2.y * edge1.z - duv1.y * edge2.z)
};
v0.tangent += tangent;
v1.tangent += tangent;
v2.tangent += tangent;
}
}
}
if ( lod0Tangents.empty() ) {
for ( auto& vert : meshlet.vertices ) {
vert.tangent = uf::vector::normalize(vert.tangent - vert.normal * uf::vector::dot(vert.normal, vert.tangent));
}
}
if ( lod0Tangents.empty() ) uf::mesh::tangents( meshlet.vertices, meshlet.indices );
size_t materialID = 0;
uf::stl::string matName = "missing_texture";

View File

@ -12,6 +12,7 @@ namespace impl {
constexpr uint32_t IMAGE_FORMAT_DXT5 = 15;
constexpr uint32_t TEXTUREFLAGS_ENVMAP = 0x00002000;
constexpr uint32_t TEXTUREFLAGS_NORMAL = 0x00000080;
#pragma pack(push, 1)
struct VTFHeader {
@ -284,5 +285,28 @@ bool ext::valve::loadVtf( pod::Image& image, const uf::stl::string& filename ) {
}
}
if ( (header->flags & impl::TEXTUREFLAGS_NORMAL) != 0 && header->highResImageFormat == impl::IMAGE_FORMAT_DXT5 ) {
size_t pixelCount = image.pixels.size() / 4;
for ( size_t i = 0; i < pixelCount; ++i ) {
uint8_t& r = image.pixels[i * 4 + 0];
uint8_t& g = image.pixels[i * 4 + 1];
uint8_t& b = image.pixels[i * 4 + 2];
uint8_t& a = image.pixels[i * 4 + 3];
float x = (a / 255.0f) * 2.0f - 1.0f;
float y = (g / 255.0f) * 2.0f - 1.0f;
y = -y;
float z = std::sqrt(std::max(1.0f - (x * x + y * y), 0.0f));
r = (uint8_t)((x * 0.5f + 0.5f) * 255.0f);
g = (uint8_t)((y * 0.5f + 0.5f) * 255.0f);
b = (uint8_t)((z * 0.5f + 0.5f) * 255.0f);
a = 255;
}
}
return true;
}

View File

@ -447,7 +447,8 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, VkCommandBuffer comm
struct PushConstant {
uint32_t pass;
uint32_t draw;
} pushConstant = { pass, draw };
uint32_t aux;
} pushConstant = { pass, draw, descriptor.aux };
vkCmdPushConstants( commandBuffer, pipelineLayout, shader->descriptor.stage, 0, sizeof(pushConstant), &pushConstant );
} else if ( !shader->pushConstants.empty() ) {
auto& pushConstant = shader->pushConstants.front();

View File

@ -29,8 +29,6 @@
#endif
namespace {
const uf::stl::string DEFERRED_MODE = "compute";
namespace postprocesses {
struct {
ext::vulkan::Buffer atomicCounter;
@ -243,25 +241,6 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
/*.layer = */0,
/*.autoBuildPipeline =*/ true
);
if ( DEFERRED_MODE == "fragment" ) {
renderTarget.addPass(
/*.*/ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
/*.colors =*/ { attachments.color, attachments.scratch, attachments.motion },
#if BARYCENTRIC
#if !BARYCENTRIC_CALCULATE
/*.inputs =*/ { attachments.id, attachments.depth, attachments.bary },
#else
/*.inputs =*/ { attachments.id, attachments.depth },
#endif
#else
/*.inputs =*/ { attachments.id, attachments.depth, attachments.uv, attachments.normal },
#endif
/*.resolve =*/{},
/*.depth = */attachments.depth,
/*.layer = */0,
/*.autoBuildPipeline =*/ false
);
}
// metadata.outputs.emplace_back(metadata.attachments["output"]);
renderTarget.initialize( device );
@ -339,34 +318,19 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
}
if ( settings::pipelines::deferred ) {
if ( DEFERRED_MODE == "compute" ) {
uf::stl::string computeShaderFilename = "comp.spv"; {
std::pair<bool, uf::stl::string> settings[] = {
{ uf::renderer::settings::pipelines::rt, "rt.comp" },
{ uf::renderer::settings::pipelines::vxgi, "vxgi.comp" },
{ msaa > 1, "msaa.comp" },
};
FOR_ARRAY( settings ) if ( settings[i].first ) computeShaderFilename = uf::string::replace( computeShaderFilename, "comp", settings[i].second );
}
computeShaderFilename = uf::io::root+"/shaders/display/deferred/comp/" + computeShaderFilename;
blitter.material.attachShader(uf::io::resolveURI(computeShaderFilename), uf::renderer::enums::Shader::COMPUTE, "deferred");
UF_MSG_DEBUG("Using deferred shader: {}", computeShaderFilename);
} else if ( DEFERRED_MODE == "fragment" ) {
uf::stl::string vertexShaderFilename = "vert.spv";
uf::stl::string fragmentShaderFilename = "frag.spv"; {
std::pair<bool, uf::stl::string> settings[] = {
{ uf::renderer::settings::pipelines::rt, "rt.frag" },
{ uf::renderer::settings::pipelines::vxgi, "vxgi.frag" },
{ msaa > 1, "msaa.frag" },
};
FOR_ARRAY( settings ) if ( settings[i].first ) fragmentShaderFilename = uf::string::replace( fragmentShaderFilename, "frag", settings[i].second );
}
fragmentShaderFilename = uf::io::root+"/shaders/display/deferred/frag/" + fragmentShaderFilename;
blitter.material.attachShader(uf::io::resolveURI(fragmentShaderFilename), uf::renderer::enums::Shader::FRAGMENT, "deferred");
UF_MSG_DEBUG("Using deferred shader: {}", fragmentShaderFilename);
uf::stl::string computeShaderFilename = "comp.spv"; {
std::pair<bool, uf::stl::string> settings[] = {
{ uf::renderer::settings::pipelines::rt, "rt.comp" },
{ uf::renderer::settings::pipelines::vxgi, "vxgi.comp" },
{ msaa > 1, "msaa.comp" },
};
FOR_ARRAY( settings ) if ( settings[i].first ) computeShaderFilename = uf::string::replace( computeShaderFilename, "comp", settings[i].second );
}
computeShaderFilename = uf::io::root+"/shaders/display/deferred/comp/" + computeShaderFilename;
blitter.material.attachShader(uf::io::resolveURI(computeShaderFilename), uf::renderer::enums::Shader::COMPUTE, "deferred");
UF_MSG_DEBUG("Using deferred shader: {}", computeShaderFilename);
auto& shader = blitter.material.getShader(DEFERRED_MODE, "deferred");
auto& shader = blitter.material.getShader("compute", "deferred");
size_t maxLights = uf::config["engine"]["scenes"]["lights"]["max"].as<size_t>(512);
size_t maxTextures2D = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as<size_t>(512);
@ -561,8 +525,8 @@ void ext::vulkan::DeferredRenderMode::build( bool resized ) {
}
// (re)bind aliases
if ( blitter.material.hasShader(DEFERRED_MODE, "deferred") ) {
auto& shader = blitter.material.getShader(DEFERRED_MODE, "deferred");
if ( blitter.material.hasShader("compute", "deferred") ) {
auto& shader = blitter.material.getShader("compute", "deferred");
shader.metadata.aliases.buffers.clear();
shader.aliasBuffer( storage.buffers.camera );
@ -587,15 +551,10 @@ void ext::vulkan::DeferredRenderMode::build( bool resized ) {
descriptor.bind.height = height;
descriptor.bind.depth = 1;
if ( settings::pipelines::deferred && blitter.material.hasShader(DEFERRED_MODE, "deferred") ) {
if ( settings::pipelines::deferred && blitter.material.hasShader("compute", "deferred") ) {
descriptor.pipeline = "deferred";
if ( DEFERRED_MODE == "fragment" ) {
descriptor.subpass = 1;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_GRAPHICS;
} else if ( DEFERRED_MODE == "compute" ) {
descriptor.subpass = 0;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
}
descriptor.subpass = 0;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
blitter.update( descriptor );
}
@ -894,48 +853,57 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// render to geometry buffers
{
size_t currentPass = 0;
size_t currentDraw = 0;
// render skybox geometry
for ( auto graphic : graphics ) {
if ( graphic->descriptor.renderMode != this->getName() ) continue;
if ( graphic->descriptor.renderTarget != 0 /*this->getName()*/ ) continue;
if ( graphic->descriptor.aux != 1 ) continue;
ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentSubpass);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[skybox][{}]", currentDraw) );
graphic->record( commandBuffer, descriptor, 0, currentDraw++, frame );
}
// clear depth buffer
if ( currentDraw > 0 ) {
VkClearAttachment clearDepth = {};
clearDepth.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
clearDepth.clearValue.depthStencil = { depthClear, 0 };
VkClearRect clearRect = {};
clearRect.rect.offset = { 0, 0 };
clearRect.rect.extent = { width, height };
clearRect.baseArrayLayer = 0;
clearRect.layerCount = metadata.eyes > 0 ? metadata.eyes : 1;
vkCmdClearAttachments(commandBuffer, 1, &clearDepth, 1, &clearRect);
}
// render normal geometry
for ( auto graphic : graphics ) {
// only draw graphics that are assigned to this type of render mode
if ( graphic->descriptor.renderMode != this->getName() ) continue;
if ( graphic->descriptor.renderTarget != 0 /*this->getName()*/ ) continue;
if ( graphic->descriptor.aux != 0 ) continue;
ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentSubpass);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", currentDraw) );
graphic->record( commandBuffer, descriptor, 0, currentDraw++, frame );
}
}
// skip deferred pass if RT is enabled, we still process geometry for a depth buffer
if ( DEFERRED_MODE == "fragment" ) {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "nextSubpass" );
vkCmdNextSubpass(commandBuffer, VK_SUBPASS_CONTENTS_INLINE); ++currentSubpass;
size_t currentPass = 0;
size_t currentDraw = 0;
{
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor; // = bindGraphicDescriptor(blitter.descriptor, currentSubpass);
descriptor.renderMode = "";
descriptor.pipeline = "deferred";
descriptor.subpass = currentSubpass;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_GRAPHICS;
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "deferred" );
blitter.record(commandBuffer, descriptor, 0, currentDraw++, frame);
}
}
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::END, "renderPass[end]" );
vkCmdEndRenderPass(commandBuffer);
#if 1
if ( settings::pipelines::deferred && DEFERRED_MODE == "compute" && blitter.material.hasShader(DEFERRED_MODE, "deferred") ) {
if ( settings::pipelines::deferred && blitter.material.hasShader("compute", "deferred") ) {
VkMemoryBarrier computeBarrier = {};
computeBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
computeBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
computeBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 1, &computeBarrier, 0, nullptr, 0, nullptr );
auto& shader = blitter.material.getShader(DEFERRED_MODE, "deferred");
auto& shader = blitter.material.getShader("compute", "deferred");
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor;
descriptor.renderMode = "";
descriptor.pipeline = "deferred";