more fixes before I lose my mind again (something something making VXGI look actually nice, correctness, fixing graphs, etc etc)

This commit is contained in:
ecker 2026-02-21 21:26:37 -06:00
parent de44a6083d
commit 012cddd37f
33 changed files with 538 additions and 499 deletions

View File

@ -31,7 +31,7 @@
"vxgi": { "vxgi": {
// "limiter": 0, // "limiter": 0,
"limiter": 0.0125, "limiter": 0.0125,
"size": 192, "size": 256,
"dispatch": 16, "dispatch": 16,
"cascades": 3, "cascades": 3,
"cascadePower": 2, "cascadePower": 2,
@ -42,8 +42,8 @@
"filtering": "LINEAR", "filtering": "LINEAR",
"shadows": 0, "shadows": 0,
"extents": { "extents": {
"min": [ -8, -8, -8 ], "min": [ -16, -4, -16 ],
"max": [ 8, 8, 8 ] "max": [ 16, 4, 16 ]
} }
}, },
"rt": { "rt": {
@ -60,7 +60,7 @@
}, },
"graph": { "graph": {
"initial buffer elements": 128, "initial buffer elements": 128,
"global storage": false "global storage": true
}, },
"ext": { "ext": {
"vulkan": { "vulkan": {

View File

@ -31,9 +31,7 @@
"gravity": [ 0, -9.81, 0 ], "gravity": [ 0, -9.81, 0 ],
"mass": 10, "mass": 10,
"type": "sphere", "type": "bounding box",
"radius": 1,
"static": false,
"recenter": false "recenter": false
}, },
"graph": { "graph": {

View File

@ -0,0 +1,17 @@
{
"assets": [],
"behaviors": [
"SoundEmitterBehavior"
],
"metadata": {
"holdable": true,
"physics": {
"gravity": [ 0, 0, 0 ],
"inertia": false,
"mass": 1,
"type": "aabb",
"recenter": false
}
}
}

View File

@ -8,8 +8,8 @@
"physics": { "physics": {
"mass": 0, "mass": 0,
"inertia": false, "inertia": false,
"type": "bounding box" // "type": "bounding box"
// "type": "mesh" "type": "mesh"
} }
} }
} }

View File

@ -244,6 +244,8 @@ ent:addHook( "entity:Use.%UID%", function( payload )
heldObject.distance = offset:norm() heldObject.distance = offset:norm()
prop:getComponent("PhysicsBody"):enableGravity(false) prop:getComponent("PhysicsBody"):enableGravity(false)
print( prop:name() )
else else
validUse = not string.matched( prop:name(), "/^worldspawn/" ) validUse = not string.matched( prop:name(), "/^worldspawn/" )
end end

View File

@ -3,7 +3,7 @@
"assets": [ "assets": [
// { "filename": "./models/mds_mcdonalds.glb" } // { "filename": "./models/mds_mcdonalds.glb" }
{ "filename": "./models/mds_mcdonalds/graph.json" }, { "filename": "./models/mds_mcdonalds/graph.json" },
{ "filename": "/burger.json", "delay": 4 } { "filename": "/burger.json", "delay": 1 }
], ],
"metadata": { "metadata": {
"graph": { "graph": {
@ -15,8 +15,9 @@
"func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } }, "func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
"func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } }, "func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
"prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
// regex matches // regex matches
// "/^prop_physics_override/": { "action": "load", "payload": { "import": "/prop.json", "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } } } },
// "/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } }, // "/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },
"/^tools\\/toolsnodraw/": { "material": { "/^tools\\/toolsnodraw/": { "material": {

View File

@ -6,7 +6,7 @@
], ],
"metadata": { "metadata": {
"light": { "light": {
"fog-0": { "fog": {
// "color": [ 0.1, 0.1, 0.1 ], // "color": [ 0.1, 0.1, 0.1 ],
// "color": [ 0.2, 0.2, 0.2 ], // "color": [ 0.2, 0.2, 0.2 ],
"color": [ 0.3, 0.3, 0.3 ], "color": [ 0.3, 0.3, 0.3 ],

View File

@ -2,47 +2,48 @@
void fog( in Ray ray, inout vec3 i, float scale ) { void fog( in Ray ray, inout vec3 i, float scale ) {
if ( ubo.settings.fog.stepScale <= 0 || ubo.settings.fog.range.x == 0 || ubo.settings.fog.range.y == 0 ) return; if ( ubo.settings.fog.stepScale <= 0 || ubo.settings.fog.range.x == 0 || ubo.settings.fog.range.y == 0 ) return;
#if FOG_RAY_MARCH #if FOG_RAY_MARCH
if ( ubo.settings.fog.stepScale <= 0 || ubo.settings.fog.range.y <= 0 ) return;
const float range = ubo.settings.fog.range.y; const float range = ubo.settings.fog.range.y;
const vec3 boundsMin = vec3(-range,-range,-range) + ray.origin; const vec3 boundsMin = ray.origin - vec3(range);
const vec3 boundsMax = vec3(range,range,range) + ray.origin; const vec3 boundsMax = ray.origin + vec3(range);
const int numSteps = int(length(boundsMax - boundsMin) * ubo.settings.fog.stepScale );
const vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, ray ); const vec2 rayBoxInfo = rayBoxDst( boundsMin, boundsMax, ray );
const float dstToBox = rayBoxInfo.x; const float dstToBox = rayBoxInfo.x;
const float dstInsideBox = rayBoxInfo.y; const float dstInsideBox = rayBoxInfo.y;
const float depth = surface.position.eye.z; const float depth = surface.position.eye.z;
const float aperture = 0; // PI * 0.5; if ( dstInsideBox < 0 || dstToBox > depth ) return;
const float coneCoefficient = 2.0 * tan(aperture * 0.5);
// march const float marchLimit = min( depth - dstToBox, dstInsideBox );
if ( 0 <= dstInsideBox && dstToBox <= depth ) { const int MAX_STEPS = 32;
float stepSize = dstInsideBox / numSteps;
float dstLimit = min( depth - dstToBox, dstInsideBox ); const float stepSize = marchLimit / float(MAX_STEPS);
float totalDensity = 0; const vec3 stepVec = ray.direction * stepSize;
float transmittance = 1; const float jitter = rand2(gl_GlobalInvocationID.xy);
float lightFactor = scale;
float coneDiameter = coneCoefficient * ray.distance; const float densityScale = ubo.settings.fog.densityScale * 0.001;
float level = aperture > 0 ? log2( coneDiameter ) : 0; const vec3 densityOffset = ubo.settings.fog.offset * 0.01;
float density = 0; const float densityThreshold = ubo.settings.fog.densityThreshold;
vec3 uvw; const float densityMult = ubo.settings.fog.densityMultiplier;
ray.distance = dstToBox; const float absorption = ubo.settings.fog.absorbtion;
while ( ray.distance < dstLimit ) {
ray.distance += stepSize; vec3 currentPos = ray.origin + (ray.direction * (dstToBox + stepSize * jitter));
ray.position = ray.origin + ray.direction * ray.distance; float transmittance = 1.0;
coneDiameter = coneCoefficient * ray.distance;
level = aperture > 0 ? log2( coneDiameter ) : 0; for ( uint j = 0; j < MAX_STEPS; ++j ) {
uvw = ray.position * ubo.settings.fog.densityScale * 0.001 + ubo.settings.fog.offset * 0.01; vec3 uvw = currentPos * densityScale + densityOffset;
density = max(0, textureLod(samplerNoise, uvw, level).r - ubo.settings.fog.densityThreshold) * ubo.settings.fog.densityMultiplier; float noiseVal = textureLod(samplerNoise, uvw, 0.0).r;
if ( density > 0 ) { float density = max(0.0, noiseVal - densityThreshold) * densityMult;
density = exp(-density * stepSize * ubo.settings.fog.absorbtion); if ( density > 0.0 ) {
transmittance *= density; float beer = exp(-density * stepSize * absorption);
lightFactor *= density; transmittance *= beer;
if ( transmittance < 0.1 ) break; if ( transmittance < 0.01 ) break;
}
} }
i.rgb = mix(ubo.settings.fog.color.rgb, i.rgb, transmittance ); currentPos += stepVec;
} }
i.rgb = mix(ubo.settings.fog.color.rgb, i.rgb, transmittance );
#endif #endif
#if FOG_BASIC #if FOG_BASIC
const vec3 color = ubo.settings.fog.color.rgb; const vec3 color = ubo.settings.fog.color.rgb;

View File

@ -2,7 +2,7 @@
void pbr() { void pbr() {
// per-surface, not per-light, compute once // per-surface, not per-light, compute once
// Freslen reflectance for a dieletric // Fresnel reflectance for a dieletric
const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic); const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic);
// outcoming light from surface to eye // outcoming light from surface to eye
const vec3 Lo = normalize( -surface.position.eye ); const vec3 Lo = normalize( -surface.position.eye );

View File

@ -1,35 +1,51 @@
// GI // GI
uint cascadeIndex( vec3 v ) { float cascadeScales[CASCADES];
float x = max3( abs( v ) ); float cascadeScalesInv[CASCADES];
for ( uint cascade = 0; cascade < CASCADES; ++cascade ) void precomputeCascades() {
if ( x / cascadePower(cascade) < 1 - voxelInfo.radianceSizeRecip ) return cascade; for ( int i = 0; i < CASCADES; ++i ) {
return CASCADES - 1; cascadeScales[i] = cascadePower(i);
cascadeScalesInv[i] = 1.0 / cascadeScales[i];
}
} }
vec4 voxelTrace( inout Ray ray, float aperture, float maxDistance ) { vec4 voxelTrace( inout Ray ray, float aperture, float maxDistance ) {
ray.origin += ray.direction * voxelInfo.radianceSizeRecip * 2 * SQRT2; ray.origin += ray.direction * voxelInfo.radianceSizeRecip * 1.5;
#if VXGI_NDC #if VXGI_NDC
ray.origin = vec3( ubo.settings.vxgi.matrix * vec4( ray.origin, 1.0 ) ); ray.origin = vec3( ubo.settings.vxgi.matrix * vec4( ray.origin, 1.0 ) );
ray.direction = vec3( ubo.settings.vxgi.matrix * vec4( ray.direction, 0.0 ) ); ray.direction = vec3( ubo.settings.vxgi.matrix * vec4( ray.direction, 0.0 ) );
uint cascade = cascadeIndex(ray.origin); float absMax = max3(abs(ray.origin));
#else #else
uint cascade = cascadeIndex( vec3( ubo.settings.vxgi.matrix * vec4( ray.origin, 1.0 ) ) ); const float voxelOrigin = vec3( ubo.settings.vxgi.matrix * vec4( ray.origin, 1.0 ) );
float absMax = max3(abs(voxelOrigin));
#endif #endif
const float granularityRecip = ubo.settings.vxgi.granularity; //2.0; // 0.25f * (CASCADES - cascade);
const float granularity = 1.0f / granularityRecip; uint cascade = 0;
const float occlusionFalloff = ubo.settings.vxgi.occlusionFalloff; //128.0f; for ( uint c = 0; c < CASCADES - 1; ++c ) {
const vec3 voxelBounds = voxelInfo.max - voxelInfo.min; if ( absMax * cascadeScalesInv[c] < (1.0 - voxelInfo.radianceSizeRecip)) {
const vec3 voxelBoundsRecip = 1.0f / voxelBounds; cascade = c;
break;
}
cascade = CASCADES - 1;
}
const float maxCascadeScale = cascadeScales[CASCADES-1];
float currentCascadeScale = cascadeScales[cascade];
float currentCascadeScaleInv = cascadeScalesInv[cascade];
const float granularity = ubo.settings.vxgi.granularity;
const float occlusionFalloff = ubo.settings.vxgi.occlusionFalloff;
const float coneCoefficient = 2.0 * tan(aperture * 0.5); const float coneCoefficient = 2.0 * tan(aperture * 0.5);
const uint maxSteps = uint(voxelInfo.radianceSize * cascadePower(CASCADES-1) * granularityRecip);
const uint maxSteps = uint(voxelInfo.radianceSize * maxCascadeScale * granularity);
const float maxRadiance = 0.90;
// box // box
const vec2 rayBoxInfoA = rayBoxDst( voxelInfo.min * cascadePower(cascade), voxelInfo.max * cascadePower(cascade), ray ); const vec2 rayBoxInfoA = rayBoxDst( voxelInfo.min * currentCascadeScale, voxelInfo.max * currentCascadeScale, ray );
const vec2 rayBoxInfoB = rayBoxDst( voxelInfo.min * cascadePower(CASCADES-1), voxelInfo.max * cascadePower(CASCADES-1), ray ); const vec2 rayBoxInfoB = rayBoxDst( voxelInfo.min * maxCascadeScale, voxelInfo.max * maxCascadeScale, ray );
const float tStart = rayBoxInfoA.x; const float tStart = rayBoxInfoA.x;
const float tEnd = maxDistance > 0 ? min(maxDistance, rayBoxInfoB.y) : rayBoxInfoB.y; const float tEnd = maxDistance > 0 ? min(maxDistance, rayBoxInfoB.y) : rayBoxInfoB.y;
const float tDelta = voxelInfo.radianceSizeRecip * granularityRecip; const float tDelta = voxelInfo.radianceSizeRecip * granularity;
const uint MIN_VOXEL_MIP_LEVEL = 1;
// marcher // marcher
ray.distance = tStart + tDelta * ubo.settings.vxgi.traceStartOffsetFactor; ray.distance = tStart + tDelta * ubo.settings.vxgi.traceStartOffsetFactor;
ray.position = vec3(0); ray.position = vec3(0);
@ -39,32 +55,39 @@ vec4 voxelTrace( inout Ray ray, float aperture, float maxDistance ) {
float coneDiameter = coneCoefficient * ray.distance; float coneDiameter = coneCoefficient * ray.distance;
float level = aperture > 0 ? log2( coneDiameter ) : 0; float level = aperture > 0 ? log2( coneDiameter ) : 0;
vec4 color = vec4(0); vec4 color = vec4(0);
float occlusion = 0.0; float occlusion = 0;
uint stepCounter = 0; uint stepCounter = 0;
while ( color.a < 1.0 && occlusion < 1.0 && ray.distance < tEnd && stepCounter++ < maxSteps ) {
float stepScale = max(1.0, coneDiameter * cascadePower(cascade)); ray.distance += tDelta * rand2(gl_GlobalInvocationID.xy);
while ( color.a < maxRadiance && occlusion < 1.0 && ray.distance < tEnd && stepCounter++ < maxSteps ) {
float stepScale = max(1.0, (coneDiameter * currentCascadeScale) * 1.5);
ray.distance += tDelta * stepScale; ray.distance += tDelta * stepScale;
// ray.distance += tDelta * cascadePower(cascade) * max(1, coneDiameter);
ray.position = ray.origin + ray.direction * ray.distance; ray.position = ray.origin + ray.direction * ray.distance;
#if VXGI_NDC absMax = max3(abs(ray.position));
uvw = ray.position; if ( absMax * currentCascadeScaleInv > 0.99 ) {
#else if ( ++cascade >= CASCADES ) break;
uvw = vec3( ubo.settings.vxgi.matrix * vec4( ray.position, 1.0 ) );
#endif currentCascadeScale = cascadeScales[cascade];
cascade = cascadeIndex( uvw ); currentCascadeScaleInv = cascadeScalesInv[cascade];
uvw = (uvw / cascadePower(cascade)) * 0.5 + 0.5;
if ( cascade >= CASCADES || uvw.x < 0.0 || uvw.y < 0.0 || uvw.z < 0.0 || uvw.x >= 1.0 || uvw.y >= 1.0 || uvw.z >= 1.0 ) break; ray.distance += tDelta * currentCascadeScale;
continue;
}
uvw = ray.position * currentCascadeScaleInv * 0.5 + 0.5;
coneDiameter = coneCoefficient * ray.distance; coneDiameter = coneCoefficient * ray.distance;
level = aperture > 0 ? log2( coneDiameter ) : 0; level = aperture > 0 ? log2( coneDiameter ) : 0;
// level = clamp(level, MIN_VOXEL_MIP_LEVEL, voxelInfo.mipmapLevels - 1);
if ( level >= voxelInfo.mipmapLevels ) break;
radiance = textureLod(voxelOutput[nonuniformEXT(cascade)], uvw.xzy, level); radiance = textureLod(voxelOutput[nonuniformEXT(cascade)], uvw.xzy, level);
color += (1.0 - color.a) * radiance;
color.rgb += (1.0 - color.a) * radiance.rgb * radiance.a;
color.a += (1.0 - color.a) * radiance.a;
occlusion += ((1.0f - occlusion) * radiance.a) / (1.0f + occlusionFalloff * coneDiameter); occlusion += ((1.0f - occlusion) * radiance.a) / (1.0f + occlusionFalloff * coneDiameter);
} }
return maxDistance > 0 ? color : vec4(color.rgb, occlusion); return maxDistance > 0 ? color : vec4(color.rgb, occlusion);
// return vec4(color.rgb, occlusion);
} }
vec4 voxelConeTrace( inout Ray ray, float aperture ) { vec4 voxelConeTrace( inout Ray ray, float aperture ) {
return voxelTrace( ray, aperture, 0 ); return voxelTrace( ray, aperture, 0 );
@ -86,9 +109,12 @@ float shadowFactorVXGI( const Light light, float def ) {
return 1.0 - voxelTrace( ray, SHADOW_APERTURE, z ).a; return 1.0 - voxelTrace( ray, SHADOW_APERTURE, z ).a;
} }
void indirectLightingVXGI() { void indirectLightingVXGI() {
precomputeCascades();
voxelInfo.radianceSize = textureSize( voxelOutput[0], 0 ).x; voxelInfo.radianceSize = textureSize( voxelOutput[0], 0 ).x;
voxelInfo.radianceSizeRecip = 1.0 / voxelInfo.radianceSize; voxelInfo.radianceSizeRecip = 1.0 / voxelInfo.radianceSize;
voxelInfo.mipmapLevels = log2(voxelInfo.radianceSize) + 1; voxelInfo.mipmapLevels = log2(voxelInfo.radianceSize) + 1;
#if VXGI_NDC #if VXGI_NDC
voxelInfo.min = vec3( -1 ); voxelInfo.min = vec3( -1 );
voxelInfo.max = vec3( 1 ); voxelInfo.max = vec3( 1 );
@ -98,53 +124,36 @@ void indirectLightingVXGI() {
voxelInfo.max = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) ); voxelInfo.max = vec3( inverseOrtho * vec4( 1, 1, 1, 1 ) );
#endif #endif
const vec3 P = surface.position.world;
const vec3 N = surface.normal.world;
#if 1
const vec3 right = normalize(orthogonal(N));
const vec3 up = normalize(cross(right, N));
const uint CONES_COUNT = 6;
const vec3 CONES[] = {
N,
normalize(N + 0.0f * right + 0.866025f * up),
normalize(N + 0.823639f * right + 0.267617f * up),
normalize(N + 0.509037f * right + -0.7006629f * up),
normalize(N + -0.50937f * right + -0.7006629f * up),
normalize(N + -0.823639f * right + 0.267617f * up),
};
#else
const vec3 ortho = normalize(orthogonal(N));
const vec3 ortho2 = normalize(cross(ortho, N));
const vec3 corner = 0.5f * (ortho + ortho2);
const vec3 corner2 = 0.5f * (ortho - ortho2);
const uint CONES_COUNT = 9;
const vec3 CONES[] = {
N,
normalize(mix(N, ortho, 0.5)),
normalize(mix(N, -ortho, 0.5)),
normalize(mix(N, ortho2, 0.5)),
normalize(mix(N, -ortho2, 0.5)),
normalize(mix(N, corner, 0.5)),
normalize(mix(N, -corner, 0.5)),
normalize(mix(N, corner2, 0.5)),
normalize(mix(N, -corner2, 0.5)),
};
#endif
const float DIFFUSE_CONE_APERTURE = 2.0f * 0.57735f;
const float DIFFUSE_INDIRECT_FACTOR = 1.0f / float(CONES_COUNT) * 0.125f;
const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * surface.material.roughness), 0.0174533f, PI); // tan( R * PI * 0.5f * 0.1f );
const float SPECULAR_INDIRECT_FACTOR = (1.0f - surface.material.metallic) * (1.0f - surface.material.roughness); // * 0.25; // 1.0f;
vec4 indirectDiffuse = vec4(0); vec4 indirectDiffuse = vec4(0);
vec4 indirectSpecular = vec4(0); vec4 indirectSpecular = vec4(0);
// outFragColor.rgb = voxelConeTrace( surface.ray, 0 ).rgb; return; const vec3 P = surface.position.world;
const vec3 N = surface.normal.world;
const vec3 right = normalize(orthogonal(N));
const vec3 up = normalize(cross(right, N));
#if 0
{
Ray ray;
ray.direction = N;
ray.origin = P + N * (voxelInfo.radianceSizeRecip * 4.0);
indirectDiffuse = voxelConeTrace(ray, 1.0f);
surface.material.occlusion += 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0);
}
#else
const uint CONES_COUNT = 4;
const vec3 CONES[] = {
normalize(N + right + up),
normalize(N - right + up),
normalize(N + right - up),
normalize(N - right - up)
};
const float DIFFUSE_CONE_APERTURE = 1.1547;
const float DIFFUSE_INDIRECT_FACTOR = 1.0f / float(CONES_COUNT) * 0.125f;
if ( DIFFUSE_INDIRECT_FACTOR > 0.0f ) { if ( DIFFUSE_INDIRECT_FACTOR > 0.0f ) {
float weight = PI * 0.25f; float weight = PI * 0.25f;
for ( uint i = 0; i < CONES_COUNT; ++i ) { for ( uint i = 0; i < CONES_COUNT; ++i ) {
@ -154,32 +163,35 @@ void indirectLightingVXGI() {
indirectDiffuse += voxelConeTrace( ray, DIFFUSE_CONE_APERTURE ) * weight; indirectDiffuse += voxelConeTrace( ray, DIFFUSE_CONE_APERTURE ) * weight;
weight = PI * 0.15f; weight = PI * 0.15f;
} }
// indirectDiffuse.rgb *= surface.material.albedo.rgb;
surface.material.occlusion += 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0); surface.material.occlusion += 1.0 - clamp(indirectDiffuse.a, 0.0, 1.0);
// outFragColor.rgb = indirectDiffuse.rgb; return;
// outFragColor.rgb = vec3(surface.material.occlusion); return;
} }
indirectDiffuse *= DIFFUSE_INDIRECT_FACTOR;
#endif
const float SPECULAR_CONE_APERTURE = clamp(tan(PI * 0.5f * surface.material.roughness), 0.0174533f, PI); // tan( R * PI * 0.5f * 0.1f );
const float SPECULAR_INDIRECT_FACTOR = (1.0f - surface.material.metallic) * (1.0f - surface.material.roughness); // * 0.25; // 1.0f;
if ( SPECULAR_INDIRECT_FACTOR > 0.0f ) { if ( SPECULAR_INDIRECT_FACTOR > 0.0f ) {
const vec3 R = reflect( normalize(P - surface.ray.origin), N ); const vec3 R = reflect( normalize(P - surface.ray.origin), N );
Ray ray; Ray ray;
ray.direction = R; ray.direction = R;
ray.origin = P; // + ray.direction; ray.origin = P; // + ray.direction;
indirectSpecular = voxelConeTrace( ray, SPECULAR_CONE_APERTURE ); indirectSpecular = voxelConeTrace( ray, SPECULAR_CONE_APERTURE );
// indirectSpecular.rgb *= surface.material.albedo.rgb;
// outFragColor.rgb = indirectSpecular.rgb; return;
} }
/*
if ( true ) {
gammaCorrect(indirectDiffuse.rgb, 1.0 / ubo.settings.bloom.gamma);
}
*/
indirectDiffuse *= DIFFUSE_INDIRECT_FACTOR;
indirectSpecular *= SPECULAR_INDIRECT_FACTOR; indirectSpecular *= SPECULAR_INDIRECT_FACTOR;
// Calculate Fresnel
{
const vec3 V = normalize(surface.ray.origin - surface.position.world);
const vec3 N = surface.normal.world;
const float NdotV = max(dot(N, V), 0.0);
const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic);
const vec3 F = fresnelSchlick(F0, NdotV);
indirectDiffuse.rgb *= (1.0 - F) * (1.0 - surface.material.metallic) * surface.material.occlusion;
}
surface.material.indirect += indirectDiffuse + indirectSpecular; surface.material.indirect += indirectDiffuse + indirectSpecular;
// outFragColor.rgb = surface.material.indirect.rgb; return;
// deferred sampling doesn't have a blended albedo buffer // deferred sampling doesn't have a blended albedo buffer
// in place we'll just cone trace behind the window // in place we'll just cone trace behind the window

View File

@ -17,12 +17,14 @@ layout (binding = 0) uniform UBO {
float threshold; float threshold;
float smoothness; float smoothness;
uint size; uint size;
uint padding1; float padding1;
float weights[32];
} ubo; } ubo;
layout (binding = 1, rgba16f) uniform volatile coherent image2D imageColor; layout (binding = 1, rgba16f) uniform image2D imageColor;
layout (binding = 2, rgba16f) uniform volatile coherent image2D imageBloom; layout (binding = 2, rgba16f) uniform image2D imageBloom;
layout (binding = 3, rgba16f) uniform volatile coherent image2D imagePingPong; layout (binding = 3, rgba16f) uniform image2D imagePingPong;
#include "../../common/macros.h" #include "../../common/macros.h"
#include "../../common/structs.h" #include "../../common/structs.h"
@ -34,35 +36,30 @@ void main() {
const ivec2 size = imageSize( imageColor ); const ivec2 size = imageSize( imageColor );
if ( texel.x >= size.x || texel.y >= size.y ) return; if ( texel.x >= size.x || texel.y >= size.y ) return;
uint kernelSize = ubo.size * 2 + 1;
float sigma = kernelSize / (6 * max(0.001f, ubo.smoothness));
float strength = 1.0f;
float scale = 1.0f;
if ( mode == 0 ) { // fill bloom if ( mode == 0 ) { // fill bloom
vec3 result = imageLoad( imageColor, texel ).rgb; vec3 result = imageLoad( imageColor, texel ).rgb;
float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722)); float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722));
if ( brightness < ubo.threshold ) result = vec3(0, 0, 0); if( brightness < ubo.threshold ) result = vec3(0.0);
imageStore( imageBloom, texel, vec4( result, 1.0 ) ); imageStore(imageBloom, texel, vec4(result, 1.0));
} else if ( mode == 1 ) { // bloom horizontal } else if ( mode == 1 ) { // bloom horizontal
vec3 result = imageLoad( imageBloom, texel ).rgb * ubo.weights[0];
vec3 result = imageLoad( imageBloom, texel ).rgb * gauss( 0, sigma ) * strength; for ( int i = 1; i < int(ubo.size); ++i ) {
for( int i = 1; i < ubo.size; ++i ) { vec3 c1 = imageLoad( imageBloom, texel + ivec2(i, 0) ).rgb;
result += imageLoad( imageBloom, texel + ivec2(i * scale, 0.0)).rgb * gauss( i * scale / ubo.size, sigma ) * strength; vec3 c2 = imageLoad( imageBloom, texel - ivec2(i, 0) ).rgb;
result += imageLoad( imageBloom, texel - ivec2(i * scale, 0.0)).rgb * gauss( i * scale / ubo.size, sigma ) * strength; result += (c1 + c2) * ubo.weights[i];
} }
// write to PingPong imageStore( imagePingPong, texel, vec4(result, 1.0) );
imageStore( imagePingPong, texel, vec4( result, 1.0 ) );
} else if ( mode == 2 ) { // bloom vertical } else if ( mode == 2 ) { // bloom vertical
vec3 result = imageLoad( imagePingPong, texel ).rgb * gauss( 0, sigma ) * strength; vec3 result = imageLoad( imagePingPong, texel ).rgb * ubo.weights[0];
for( int i = 1; i < ubo.size; ++i ) { for( int i = 1; i < int(ubo.size); ++i ) {
result += imageLoad( imagePingPong, texel + ivec2(0.0, i * scale)).rgb * gauss( i * scale / ubo.size, sigma ) * strength; vec3 c1 = imageLoad( imagePingPong, texel + ivec2(0, i) ).rgb;
result += imageLoad( imagePingPong, texel - ivec2(0.0, i * scale)).rgb * gauss( i * scale / ubo.size, sigma ) * strength; vec3 c2 = imageLoad( imagePingPong, texel - ivec2(0, i) ).rgb;
result += (c1 + c2) * ubo.weights[i];
} }
// write to Bloom imageStore(imageBloom, texel, vec4(result, 1.0));
imageStore( imageBloom, texel, vec4( result, 1.0 ) );
} else if ( mode == 3 ) { // combine } else if ( mode == 3 ) { // combine
vec3 result = imageLoad( imageColor, texel ).rgb + imageLoad( imageBloom, texel ).rgb; vec3 base = imageLoad( imageColor, texel ).rgb;
imageStore( imageColor, texel, vec4( result, 1.0 ) ); vec3 bloom = imageLoad( imageBloom, texel ).rgb;
imageStore( imageColor, texel, vec4(base + bloom, 1.0) );
} }
} }

View File

@ -50,9 +50,9 @@ layout (constant_id = 1) const uint CUBEMAPS = 128;
#endif #endif
layout(binding = 7, rgba16f) uniform volatile coherent image2D imageColor; layout(binding = 7, rgba16f) uniform writeonly image2D imageColor;
layout(binding = 8, rgba16f) uniform volatile coherent image2D imageBright; layout(binding = 8, rgba16f) uniform writeonly image2D imageBright;
layout(binding = 9, rg16f) uniform volatile coherent image2D imageMotion; layout(binding = 9, rg16f) uniform writeonly image2D imageMotion;
layout( push_constant ) uniform PushBlock { layout( push_constant ) uniform PushBlock {
uint pass; uint pass;

View File

@ -17,7 +17,7 @@ layout( push_constant ) uniform PushBlock {
} PushConstant; } PushConstant;
layout (binding = 3) uniform sampler2D inImage[MIPS]; layout (binding = 3) uniform sampler2D inImage[MIPS];
layout (binding = 4, r32f) uniform volatile coherent image2D outImage[MIPS]; layout (binding = 4, r32f) uniform writeonly image2D outImage[MIPS];
void main() { void main() {
vec2 imageSize = imageSize(outImage[PushConstant.pass]); vec2 imageSize = imageSize(outImage[PushConstant.pass]);

View File

@ -10,7 +10,7 @@ layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
#define VXGI 1 #define VXGI 1
#define MAX_CUBEMAPS CUBEMAPS #define MAX_CUBEMAPS CUBEMAPS
#define GAMMA_CORRECT 0 #define GAMMA_CORRECT 1
#define PBR 0 #define PBR 0
#define LAMBERT 1 #define LAMBERT 1
@ -52,16 +52,16 @@ layout (binding = 8) uniform sampler2D samplerTextures[TEXTURES];
layout (binding = 9) uniform samplerCube samplerCubemaps[CUBEMAPS]; layout (binding = 9) uniform samplerCube samplerCubemaps[CUBEMAPS];
layout (binding = 10) uniform sampler3D samplerNoise; layout (binding = 10) uniform sampler3D samplerNoise;
layout (binding = 11, r32ui) uniform volatile coherent uimage3D voxelDrawId[CASCADES]; layout (binding = 11, r32ui) uniform readonly uimage3D voxelDrawId[CASCADES];
layout (binding = 12, r32ui) uniform volatile coherent uimage3D voxelInstanceId[CASCADES]; layout (binding = 12, r32ui) uniform readonly uimage3D voxelInstanceId[CASCADES];
layout (binding = 13, r32ui) uniform volatile coherent uimage3D voxelNormalX[CASCADES]; layout (binding = 13, r32ui) uniform readonly uimage3D voxelNormalX[CASCADES];
layout (binding = 14, r32ui) uniform volatile coherent uimage3D voxelNormalY[CASCADES]; layout (binding = 14, r32ui) uniform readonly uimage3D voxelNormalY[CASCADES];
layout (binding = 15, r32ui) uniform volatile coherent uimage3D voxelRadianceR[CASCADES]; layout (binding = 15, r32ui) uniform readonly uimage3D voxelRadianceR[CASCADES];
layout (binding = 16, r32ui) uniform volatile coherent uimage3D voxelRadianceG[CASCADES]; layout (binding = 16, r32ui) uniform readonly uimage3D voxelRadianceG[CASCADES];
layout (binding = 17, r32ui) uniform volatile coherent uimage3D voxelRadianceB[CASCADES]; layout (binding = 17, r32ui) uniform readonly uimage3D voxelRadianceB[CASCADES];
layout (binding = 18, r32ui) uniform volatile coherent uimage3D voxelRadianceA[CASCADES]; layout (binding = 18, r32ui) uniform readonly uimage3D voxelRadianceA[CASCADES];
layout (binding = 19, r32ui) uniform volatile coherent uimage3D voxelCount[CASCADES]; layout (binding = 19, r32ui) uniform readonly uimage3D voxelCount[CASCADES];
layout (binding = 20, rgba8) uniform volatile coherent image3D voxelOutput[CASCADES]; layout (binding = 20, rgba8) uniform writeonly image3D voxelOutput[CASCADES];
#include "../../common/functions.h" #include "../../common/functions.h"
#include "../../common/light.h" #include "../../common/light.h"

View File

@ -52,7 +52,7 @@ layout (std140, binding = 15) readonly buffer Lights {
Light lights[]; Light lights[];
}; };
layout (binding = 16, rgba8) uniform volatile coherent image3D outAlbedos; layout (binding = 16, rgba8) uniform writeonly image3D outAlbedos;
#if RT #if RT
layout (binding = 17) uniform accelerationStructureEXT tlas; layout (binding = 17) uniform accelerationStructureEXT tlas;

View File

@ -37,16 +37,16 @@ layout (std140, binding = 12) readonly buffer Lights {
Light lights[]; Light lights[];
}; };
layout (binding = 13, r32ui) uniform volatile coherent uimage3D voxelDrawId[CASCADES]; layout (binding = 13, r32ui) uniform volatile uimage3D voxelDrawId[CASCADES];
layout (binding = 14, r32ui) uniform volatile coherent uimage3D voxelInstanceId[CASCADES]; layout (binding = 14, r32ui) uniform volatile uimage3D voxelInstanceId[CASCADES];
layout (binding = 15, r32ui) uniform volatile coherent uimage3D voxelNormalX[CASCADES]; layout (binding = 15, r32ui) uniform volatile uimage3D voxelNormalX[CASCADES];
layout (binding = 16, r32ui) uniform volatile coherent uimage3D voxelNormalY[CASCADES]; layout (binding = 16, r32ui) uniform volatile uimage3D voxelNormalY[CASCADES];
layout (binding = 17, r32ui) uniform volatile coherent uimage3D voxelRadianceR[CASCADES]; layout (binding = 17, r32ui) uniform volatile uimage3D voxelRadianceR[CASCADES];
layout (binding = 18, r32ui) uniform volatile coherent uimage3D voxelRadianceG[CASCADES]; layout (binding = 18, r32ui) uniform volatile uimage3D voxelRadianceG[CASCADES];
layout (binding = 19, r32ui) uniform volatile coherent uimage3D voxelRadianceB[CASCADES]; layout (binding = 19, r32ui) uniform volatile uimage3D voxelRadianceB[CASCADES];
layout (binding = 20, r32ui) uniform volatile coherent uimage3D voxelRadianceA[CASCADES]; layout (binding = 20, r32ui) uniform volatile uimage3D voxelRadianceA[CASCADES];
layout (binding = 21, r32ui) uniform volatile coherent uimage3D voxelCount[CASCADES]; layout (binding = 21, r32ui) uniform volatile uimage3D voxelCount[CASCADES];
layout (binding = 22, rgba8) uniform volatile coherent image3D voxelOutput[CASCADES]; layout (binding = 22, rgba8) uniform writeonly image3D voxelOutput[CASCADES];
layout (location = 0) flat in uvec4 inId; layout (location = 0) flat in uvec4 inId;
layout (location = 1) flat in vec4 inPOS0; layout (location = 1) flat in vec4 inPOS0;

View File

@ -57,10 +57,11 @@ void main(){
#endif #endif
const uint CASCADE = inId[0].w; const uint CASCADE = inId[0].w;
const float power = cascadePower(CASCADE);
vec3 P[3] = { vec3 P[3] = {
vec3( ubo.voxel * vec4( inPosition[0], 1 ) ) / cascadePower(CASCADE), vec3( ubo.voxel * vec4( inPosition[0], 1 ) ) / power,
vec3( ubo.voxel * vec4( inPosition[1], 1 ) ) / cascadePower(CASCADE), vec3( ubo.voxel * vec4( inPosition[1], 1 ) ) / power,
vec3( ubo.voxel * vec4( inPosition[2], 1 ) ) / cascadePower(CASCADE), vec3( ubo.voxel * vec4( inPosition[2], 1 ) ) / power,
}; };
#pragma unroll 3 #pragma unroll 3

View File

@ -32,7 +32,7 @@ layout( push_constant ) uniform PushBlock {
layout (binding = 0) uniform accelerationStructureEXT tlas; layout (binding = 0) uniform accelerationStructureEXT tlas;
layout (binding = 1, rgba32f) uniform volatile coherent image2D outImage; layout (binding = 1, rgba32f) uniform image2D outImage;
layout (binding = 2) uniform UBO { layout (binding = 2) uniform UBO {
EyeMatrices eyes[2]; EyeMatrices eyes[2];

View File

@ -29,8 +29,6 @@ namespace pod {
// Render information // Render information
uf::stl::vector<uf::stl::string> primitives; // uf::stl::vector<uf::stl::string> primitives; //
uf::stl::vector<uf::stl::string> instances; // ugh
uf::stl::vector<uf::stl::string> drawCommands; // ugh
uf::stl::vector<uf::stl::string> meshes; // uf::stl::vector<uf::stl::string> meshes; //
uf::stl::vector<uf::stl::string> images; // uf::stl::vector<uf::stl::string> images; //
@ -81,10 +79,8 @@ namespace pod {
// Local storage, used for save/load // Local storage, used for save/load
struct Storage { struct Storage {
uf::stl::KeyMap<pod::Instance> instances; // should be unused but is a counter for global instance IDs because I cannot for the life of me figure out a working way to remap otherwise uf::stl::KeyMap<uf::stl::vector<pod::Instance::Addresses>> instanceAddresses;
uf::stl::KeyMap<pod::Instance::Addresses> instanceAddresses;
uf::stl::KeyMap<uf::stl::vector<pod::Primitive>> primitives; uf::stl::KeyMap<uf::stl::vector<pod::Primitive>> primitives;
uf::stl::KeyMap<uf::stl::vector<pod::DrawCommand>> drawCommands; // unused
uf::stl::KeyMap<uf::Mesh> meshes; uf::stl::KeyMap<uf::Mesh> meshes;
uf::stl::KeyMap<uf::Image> images; uf::stl::KeyMap<uf::Image> images;
@ -137,7 +133,7 @@ namespace uf {
pod::Matrix4f UF_API matrix( pod::Graph&, int32_t ); pod::Matrix4f UF_API matrix( pod::Graph&, int32_t );
// void UF_API process( uf::Object& entity ); // void UF_API process( uf::Object& entity );
void UF_API initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh ); void UF_API initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh, uf::stl::vector<pod::Instance::Addresses>& );
void UF_API process( pod::Graph& graph ); void UF_API process( pod::Graph& graph );
void UF_API process( pod::Graph& graph, int32_t, uf::Object& parent ); void UF_API process( pod::Graph& graph, int32_t, uf::Object& parent );
void UF_API reload( pod::Graph& ); void UF_API reload( pod::Graph& );

View File

@ -483,7 +483,7 @@ T& uf::vector::negate_( T& vector ) {
template<typename T> template<typename T>
T& uf::vector::normalize_( T& vector ) { T& uf::vector::normalize_( T& vector ) {
typename T::type_t norm = uf::vector::norm(vector); typename T::type_t norm = uf::vector::norm(vector);
return ( norm == 0 ) ? T{} : ( vector = uf::vector::divide((const T&) vector, norm) ); return ( norm < 1.0e-6 ) ? T{} : ( vector = uf::vector::divide((const T&) vector, norm) );
} }
template<typename T> template<typename T>
T uf::vector::min( const T& left, const T& right ) { T uf::vector::min( const T& left, const T& right ) {
@ -679,7 +679,7 @@ T uf::vector::normalize( const T& vector ) {
} }
#endif #endif
typename T::type_t norm = uf::vector::norm(vector); typename T::type_t norm = uf::vector::norm(vector);
if ( norm == 0 ) return vector; if ( norm < 1.0e-6 ) return vector;
#if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD #if UF_ENV_DREAMCAST && UF_ENV_DREAMCAST_SIMD
if constexpr ( std::is_same_v<T,float> ) { if constexpr ( std::is_same_v<T,float> ) {
return uf::vector::multiply(vector, MATH_fsrra(norm)); return uf::vector::multiply(vector, MATH_fsrra(norm));

View File

@ -17,7 +17,9 @@ namespace uf {
T& operator[]( const Key& key ); T& operator[]( const Key& key );
void reserve( size_t i ); void reserve( size_t i );
size_t id( const Key& );
uf::stl::vector<T> flatten() const; uf::stl::vector<T> flatten() const;
uf::stl::vector<T> flattenByIndex() const;
void clear(); void clear();
}; };
} }
@ -40,6 +42,19 @@ void uf::stl::KeyMap<T,Key>::reserve( size_t i ) {
map.reserve(i); map.reserve(i);
} }
template<typename T, typename Key>
size_t uf::stl::KeyMap<T,Key>::id( const Key& key ) {
if ( indices.count( key ) > 0 ) return indices[key];
size_t newIndex = keys.size();
indices[key] = newIndex;
keys.emplace_back( key );
map[key];
return newIndex;
}
template<typename T, typename Key> template<typename T, typename Key>
void uf::stl::KeyMap<T,Key>::clear() { void uf::stl::KeyMap<T,Key>::clear() {
keys.clear(); keys.clear();
@ -58,5 +73,16 @@ uf::stl::vector<T> uf::stl::KeyMap<T,Key>::flatten() const {
res.emplace_back(map.at(key)); res.emplace_back(map.at(key));
} }
} }
return res;
}
template<typename T, typename Key>
uf::stl::vector<T> uf::stl::KeyMap<T,Key>::flattenByIndex() const {
vector<T> res(indices.size());
for (auto& [key, index] : indices) {
res[index] = map.at(key);
}
return res; return res;
} }

View File

@ -947,6 +947,8 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic
float smoothness; float smoothness;
uint32_t size; uint32_t size;
uint32_t padding; uint32_t padding;
float weights[32];
}; };
UniformDescriptor uniforms = { UniformDescriptor uniforms = {
@ -954,6 +956,24 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic
.smoothness = metadata.bloom.smoothness, .smoothness = metadata.bloom.smoothness,
.size = metadata.bloom.size, .size = metadata.bloom.size,
}; };
uint kernelSize = uniforms.size * 2 + 1;
float sigma = kernelSize / (6 * std::max(0.001f, uniforms.smoothness));
float sum = 0;
uf::stl::vector<float> tempWeights(uniforms.size);
tempWeights[0] = (1.0f / (sqrtf(2.0f * M_PI) * sigma));
sum = tempWeights[0];
for ( auto i = 1; i < uniforms.size; ++i ) {
float x = (float) i;
tempWeights[i] = (expf(-(x * x) / (2.0f * sigma * sigma)) / (sqrtf(2.0f * M_PI) * sigma));
sum += 2.0f * tempWeights[i];
}
// normalize
for ( auto i = 0; i < uniforms.size; ++i ) uniforms.weights[i] = tempWeights[i] / sum;
metadata.bloom.outOfDate = false; metadata.bloom.outOfDate = false;
if ( shader.hasUniform("UBO") ) shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); if ( shader.hasUniform("UBO") ) shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") );
} }

View File

@ -440,13 +440,9 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const
auto name = key + value["name"].as<uf::stl::string>(); auto name = key + value["name"].as<uf::stl::string>();
// UF_MSG_DEBUG("{}", name); // UF_MSG_DEBUG("{}", name);
storage.primitives[name] = decodePrimitives( value, graph ); storage.primitives[name] = decodePrimitives( value, graph );
graph.primitives.emplace_back(name); storage.instanceAddresses[name] = {};
uf::stl::vector<pod::DrawCommand> drawCommands; graph.primitives.emplace_back(name);
for ( auto& primitive : storage.primitives[name] ) {
drawCommands.emplace_back( primitive.drawCommand );
}
storage.drawCommands[name] = std::move(drawCommands);
}); });
UF_DEBUG_TIMER_MULTITRACE("Read primitives."); UF_DEBUG_TIMER_MULTITRACE("Read primitives.");
#if UF_ENV_DREAMCAST #if UF_ENV_DREAMCAST
@ -506,6 +502,9 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const
// UF_MSG_DEBUG("{}", name); // UF_MSG_DEBUG("{}", name);
storage.textures[name] = decodeTexture( value, graph ); storage.textures[name] = decodeTexture( value, graph );
graph.textures.emplace_back(name); graph.textures.emplace_back(name);
// pre-allocate
storage.texture2Ds[name];
}); });
UF_DEBUG_TIMER_MULTITRACE("Read texture information"); UF_DEBUG_TIMER_MULTITRACE("Read texture information");
#if UF_ENV_DREAMCAST #if UF_ENV_DREAMCAST

View File

@ -44,11 +44,16 @@ namespace {
return hash; return hash;
} }
uf::stl::string objectKey( pod::Node& node ){ size_t allocateObjectID( pod::Graph::Storage& storage ) {
return std::to_string(node.object); return storage.entities.keys.size();
} }
uf::stl::string instanceKey( uint32_t instanceID ) { size_t allocateInstanceID( pod::Graph::Storage& storage, const uf::stl::string& name ) {
return std::to_string( instanceID ); size_t instanceID = 0;
for ( auto& key : storage.primitives.keys ) {
if ( key == name ) break;
instanceID += storage.primitives.map[key].size();
}
return instanceID;
} }
pod::Graph::Storage& getGraphStorage( uf::Object& object ) { pod::Graph::Storage& getGraphStorage( uf::Object& object ) {
@ -523,52 +528,49 @@ namespace {
shader.aliasBuffer( "light", storage.buffers.light ); shader.aliasBuffer( "light", storage.buffers.light );
} }
} }
#endif
}
// rt pipeline void bindInstanceAddresses( uf::renderer::Graphic& graphic, uf::Mesh& mesh, uf::stl::vector<pod::Instance::Addresses>& addresses ) {
// to-do: segregate out buffer updating code if ( !uf::renderer::settings::invariant::deviceAddressing || !mesh.indirect.count ) return;
addresses.resize( mesh.indirect.count );
// grab addresses pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer( mesh.indirect ).data();
if ( uf::renderer::settings::invariant::deviceAddressing ) { for ( size_t drawID = 0; drawID < mesh.indirect.count; ++drawID ) {
pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer( mesh.indirect ).data(); auto& drawCommand = drawCommands[drawID];
for ( size_t drawID = 0; drawID < mesh.indirect.count; ++drawID ) { auto instanceID = drawCommand.instanceID;
auto& drawCommand = drawCommands[drawID];
auto instanceID = drawCommand.instanceID;
auto instanceKeyName = ::instanceKey( instanceID ); // it *should* be fine as the instance IDs are already objective to the scene graph
auto& instanceAddresses = storage.instanceAddresses[instanceKeyName]; auto& instanceAddresses = addresses[drawID]; // THIS IS WRONG (to-do: actually use instanceIDs)
if ( mesh.vertex.count ) { if ( mesh.vertex.count ) {
if ( mesh.isInterleaved( mesh.vertex ) ) { if ( mesh.isInterleaved( mesh.vertex ) ) {
instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress(); instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress();
} else { } else {
for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) {
if ( attribute.buffer < 0 ) continue; if ( attribute.buffer < 0 ) continue;
if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress(); if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress();
else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress(); else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress();
}
} }
} }
if ( mesh.index.count ) { }
if ( mesh.isInterleaved( mesh.index ) ) instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.interleaved).getAddress(); if ( mesh.index.count ) {
else instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress(); if ( mesh.isInterleaved( mesh.index ) ) instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.interleaved).getAddress();
} else instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress();
}
if ( mesh.indirect.count ) { if ( mesh.indirect.count ) {
if ( mesh.isInterleaved( mesh.indirect ) ) instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.interleaved).getAddress(); if ( mesh.isInterleaved( mesh.indirect ) ) instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.interleaved).getAddress();
else instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress(); else instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress();
instanceAddresses.drawID = drawID;
}
instanceAddresses.drawID = drawID;
} }
} }
#endif
} }
} }
@ -683,7 +685,7 @@ UF_VERTEX_INTERPOLATE(uf::graph::mesh::Skinned_u16q, {
return t < 0.5 ? p1 : p2; return t < 0.5 ? p1 : p2;
}) })
void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh ) { void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh, uf::stl::vector<pod::Instance::Addresses>& addresses ) {
auto& scene = uf::scene::getCurrentScene(); auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>(); auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
auto& sceneMetadataJson = scene.getComponent<uf::Serializer>(); auto& sceneMetadataJson = scene.getComponent<uf::Serializer>();
@ -730,6 +732,7 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M
::bindTextures( graphic ); ::bindTextures( graphic );
::bindShaders( graph, entity, mesh ); ::bindShaders( graph, entity, mesh );
::bindBuffers( graphic, mesh ); ::bindBuffers( graphic, mesh );
::bindInstanceAddresses( graphic, mesh, addresses );
graphic.process = true; graphic.process = true;
} }
@ -790,12 +793,6 @@ void uf::graph::process( pod::Graph& graph ) {
uf::stl::unordered_map<size_t, size_t> lightmapIDs; uf::stl::unordered_map<size_t, size_t> lightmapIDs;
uint32_t lightmapCount = 0; uint32_t lightmapCount = 0;
for ( auto& name : graph.instances ) {
auto& instance = storage.instances[name];
filenames[instance.auxID] = uf::string::replace(UF_GRAPH_DEFAULT_LIGHTMAP, "%i", std::to_string(instance.auxID));
lightmapCount = std::max( lightmapCount, instance.auxID + 1 );
}
for ( auto& name : graph.primitives ) { for ( auto& name : graph.primitives ) {
auto& primitives = storage.primitives[name]; auto& primitives = storage.primitives[name];
for ( auto& primitive : primitives ) { for ( auto& primitive : primitives ) {
@ -869,11 +866,6 @@ void uf::graph::process( pod::Graph& graph ) {
} }
} }
for ( auto& name : graph.instances ) {
auto& instance = storage.instances[name];
if ( lightmapIDs.count( instance.auxID ) == 0 ) continue;
instance.lightmapID = lightmapIDs[instance.auxID];
}
for ( auto& name : graph.primitives ) { for ( auto& name : graph.primitives ) {
auto& primitives = storage.primitives[name]; auto& primitives = storage.primitives[name];
for ( auto& primitive : primitives ) { for ( auto& primitive : primitives ) {
@ -886,12 +878,6 @@ void uf::graph::process( pod::Graph& graph ) {
// setup textures // setup textures
storage.texture2Ds.reserve( storage.images.map.size() ); storage.texture2Ds.reserve( storage.images.map.size() );
// not having this block will cause our images and texture2Ds to be ordered out of sync
for ( auto& key : graph.images ) {
storage.images[key];
storage.texture2Ds[key];
}
// figure out what texture is what exactly // figure out what texture is what exactly
UF_DEBUG_TIMER_MULTITRACE("Determining format of textures"); UF_DEBUG_TIMER_MULTITRACE("Determining format of textures");
for ( auto& key : graph.materials ) { for ( auto& key : graph.materials ) {
@ -951,7 +937,7 @@ void uf::graph::process( pod::Graph& graph ) {
} }
// patch materials/textures // patch materials/textures
UF_DEBUG_TIMER_MULTITRACE("Remapping patching/textures"); UF_DEBUG_TIMER_MULTITRACE("Patching textures/materials");
for ( auto& name : graph.materials ) { for ( auto& name : graph.materials ) {
auto& material = storage.materials[name]; auto& material = storage.materials[name];
auto tag = ext::json::find( name, graphMetadataJson["tags"] ); auto tag = ext::json::find( name, graphMetadataJson["tags"] );
@ -1021,7 +1007,7 @@ void uf::graph::process( pod::Graph& graph ) {
} }
// remap textures->images IDs // remap textures->images IDs
UF_DEBUG_TIMER_MULTITRACE("Remapping textures"); UF_DEBUG_TIMER_MULTITRACE("Remapping texture -> image IDs");
for ( auto& name : graph.textures ) { for ( auto& name : graph.textures ) {
auto& texture = storage.textures[name]; auto& texture = storage.textures[name];
auto& keys = storage.images.keys; auto& keys = storage.images.keys;
@ -1034,7 +1020,7 @@ void uf::graph::process( pod::Graph& graph ) {
} }
// remap materials->texture IDs // remap materials->texture IDs
UF_DEBUG_TIMER_MULTITRACE("Remapping materials"); UF_DEBUG_TIMER_MULTITRACE("Remapping material -> texture IDs");
for ( auto& name : graph.materials ) { for ( auto& name : graph.materials ) {
auto& material = storage.materials[name]; auto& material = storage.materials[name];
auto& keys = storage.textures.keys; auto& keys = storage.textures.keys;
@ -1047,82 +1033,45 @@ void uf::graph::process( pod::Graph& graph ) {
ID = indices[needle]; ID = indices[needle];
} }
} }
// remap instance variables // remap instance variables
UF_DEBUG_TIMER_MULTITRACE("Remapping instances"); UF_DEBUG_TIMER_MULTITRACE("Remapping instance -> material IDs");
for ( auto& name : graph.instances ) { for ( auto& name : graph.primitives ) {
auto& instance = storage.instances[name]; for ( auto& primitive : storage.primitives[name] ) {
auto& instance = primitive.instance;
if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) {
auto& keys = storage.materials.keys;
auto& indices = storage.materials.indices;
if ( !(0 <= instance.materialID && instance.materialID < graph.materials.size()) ) continue; if ( 0 <= instance.materialID && instance.materialID < graph.materials.size() ) {
auto& keys = storage.materials.keys;
auto& needle = graph.materials[instance.materialID]; auto& indices = storage.materials.indices;
instance.materialID = indices[needle];
}
if ( 0 <= instance.lightmapID && instance.lightmapID < graph.textures.size() ) {
auto& keys = storage.textures.keys;
auto& indices = storage.textures.indices;
if ( !(0 <= instance.lightmapID && instance.lightmapID < graph.textures.size()) ) continue;
auto& needle = graph.textures[instance.lightmapID];
instance.lightmapID = indices[needle];
}
#if 0
// i genuinely dont remember what this is used for
if ( 0 <= instance.imageID && instance.imageID < graph.images.size() ) {
auto& keys = storage.images.keys;
auto it = std::find( keys.begin(), keys.end(), graph.images[instance.imageID] );
UF_ASSERT( it != keys.end() );
instance.imageID = it - keys.begin();
}
#endif
// remap a skinID as an actual jointID
if ( 0 <= instance.jointID && instance.jointID < graph.skins.size() ) {
auto& name = graph.skins[instance.jointID];
instance.jointID = 0;
for ( auto key : storage.joints.keys ) {
if ( key == name ) break;
auto& joints = storage.joints[key];
instance.jointID += joints.size();
}
}
}
// remap draw commands
#if 0
UF_DEBUG_TIMER_MULTITRACE("Remapping drawCommands");
for ( auto& name : graph.drawCommands ) {
auto& drawCommands = storage.drawCommands[name];
for ( auto& drawCommand : drawCommands ) {
if ( 0 <= drawCommand.instanceID && drawCommand.instanceID < graph.instances.size() ) {
auto& keys = storage.instances.keys;
auto& indices = storage.instances.indices;
if ( !(0 <= drawCommand.instanceID && drawCommand.instanceID < graph.instances.size()) ) continue; if ( !(0 <= instance.materialID && instance.materialID < graph.materials.size()) ) continue;
auto& needle = graph.instances[drawCommand.instanceID]; auto& needle = graph.materials[instance.materialID];
#if 1 instance.materialID = indices[needle];
drawCommand.instanceID = indices[needle]; }
#elif 1 if ( 0 <= instance.lightmapID && instance.lightmapID < graph.textures.size() ) {
for ( size_t i = 0; i < keys.size(); ++i ) { auto& keys = storage.textures.keys;
if ( keys[i] != needle ) continue; auto& indices = storage.textures.indices;
drawCommand.instanceID = i;
break; if ( !(0 <= instance.lightmapID && instance.lightmapID < graph.textures.size()) ) continue;
auto& needle = graph.textures[instance.lightmapID];
instance.lightmapID = indices[needle];
}
// remap a skinID as an actual jointID
if ( 0 <= instance.jointID && instance.jointID < graph.skins.size() ) {
auto& name = graph.skins[instance.jointID];
instance.jointID = 0;
for ( auto key : storage.joints.keys ) {
if ( key == name ) break;
auto& joints = storage.joints[key];
instance.jointID += joints.size();
} }
#else
auto it = std::find( keys.begin(), keys.end(), needle );
UF_ASSERT( it != keys.end() );
drawCommand.instanceID = it - keys.begin();
#endif
} }
} }
} }
#endif /*
if ( graphMetadataJson["debug"]["print"]["lights"].as<bool>() ) { if ( graphMetadataJson["debug"]["print"]["lights"].as<bool>() ) {
UF_MSG_DEBUG("Lights: {}", graph.lights.size()); UF_MSG_DEBUG("Lights: {}", graph.lights.size());
for ( auto& pair : graph.lights ) { for ( auto& pair : graph.lights ) {
@ -1135,17 +1084,6 @@ void uf::graph::process( pod::Graph& graph ) {
UF_MSG_DEBUG("\tMesh: {}", name); UF_MSG_DEBUG("\tMesh: {}", name);
} }
} }
if ( graphMetadataJson["debug"]["print"]["instances"].as<bool>() ) {
UF_MSG_DEBUG("Instances: {}", graph.instances.size());
for ( auto& name : graph.instances ) {
auto& instance = storage.instances[name];
UF_MSG_DEBUG("\tInstance: {} | {} | {}", name,
instance.materialID,
instance.lightmapID
);
}
}
if ( graphMetadataJson["debug"]["print"]["materials"].as<bool>() ) { if ( graphMetadataJson["debug"]["print"]["materials"].as<bool>() ) {
UF_MSG_DEBUG("Materials: {}", graph.materials.size()); UF_MSG_DEBUG("Materials: {}", graph.materials.size());
for ( auto& name : graph.materials ) { for ( auto& name : graph.materials ) {
@ -1166,6 +1104,7 @@ void uf::graph::process( pod::Graph& graph ) {
UF_MSG_DEBUG("\tImage: {}", name); UF_MSG_DEBUG("\tImage: {}", name);
} }
} }
*/
UF_DEBUG_TIMER_MULTITRACE("Updating master graph"); UF_DEBUG_TIMER_MULTITRACE("Updating master graph");
#if UF_GRAPH_EXTENDED #if UF_GRAPH_EXTENDED
@ -1173,7 +1112,6 @@ void uf::graph::process( pod::Graph& graph ) {
#endif #endif
uf::graph::reload(); uf::graph::reload();
storage.instanceAddresses.keys = storage.instances.keys;
UF_DEBUG_TIMER_MULTITRACE_END("Processed graph."); UF_DEBUG_TIMER_MULTITRACE_END("Processed graph.");
} }
void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) { void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) {
@ -1319,30 +1257,32 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
} }
// what a mess // what a mess
node.object = storage.entities.keys.size();
auto objectKeyName = ::objectKey( node );
storage.entities[objectKeyName] = &entity;
auto& objectData = storage.objects[objectKeyName];
auto model = uf::transform::model( transform ); auto model = uf::transform::model( transform );
objectData.model = model;
objectData.previous = model;
// //
if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) { if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) {
auto model = uf::transform::model( transform ); {
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,
};
}
auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; auto& mesh = storage.meshes.map[graph.meshes[node.mesh]];
auto& primitives = storage.primitives.map[graph.primitives[node.mesh]]; auto& primitives = storage.primitives.map[graph.primitives[node.mesh]];
auto& instanceAddresses = storage.instanceAddresses.map[graph.primitives[node.mesh]];
pod::Instance::Bounds bounds = {}; pod::Instance::Bounds bounds = {};
size_t baseInstanceID = ::allocateInstanceID( storage, graph.primitives[node.mesh] );
// setup instances // setup instances
for ( auto i = 0; i < primitives.size(); ++i ) { for ( auto i = 0; i < primitives.size(); ++i ) {
auto& primitive = primitives[i]; auto& primitive = primitives[i];
auto& instance = primitive.instance;
size_t instanceID = storage.instances.keys.size(); size_t instanceID = baseInstanceID + i;
auto instanceKeyName = graph.instances.emplace_back(std::to_string(instanceID));
auto& instance = storage.instances[instanceKeyName];
instance = primitive.instance;
instance.objectID = node.object; instance.objectID = node.object;
instance.jointID = graphMetadataJson["renderer"]["skinned"].as<bool>() ? 0 : -1; instance.jointID = graphMetadataJson["renderer"]["skinned"].as<bool>() ? 0 : -1;
@ -1360,7 +1300,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
} }
#if !UF_GRAPH_EXTENDED #if !UF_GRAPH_EXTENDED
if ( graphMetadataJson["renderer"]["render"].as<bool>() ) { if ( graphMetadataJson["renderer"]["render"].as<bool>() ) {
uf::graph::initializeGraphics( graph, entity, mesh ); uf::graph::initializeGraphics( graph, entity, mesh, addresses );
} }
#endif #endif
@ -1452,29 +1392,67 @@ void uf::graph::tick( uf::Object& object ) {
} }
bool uf::graph::tick( pod::Graph::Storage& storage ) { bool uf::graph::tick( pod::Graph::Storage& storage ) {
bool rebuild = false; bool rebuild = false;
uf::stl::vector<pod::Instance> instances = storage.instances.flatten();
uf::stl::vector<pod::Instance::Addresses> instanceAddresses = storage.instanceAddresses.flatten();
uf::stl::vector<pod::Matrix4f> joints; joints.reserve(storage.joints.map.size());
uf::stl::vector<pod::Instance::Object> objects = storage.objects.flatten();
static thread_local uf::stl::vector<pod::Instance> instances;
static thread_local uf::stl::vector<pod::Instance::Addresses> instanceAddresses;
static thread_local uf::stl::vector<pod::Matrix4f> joints;
static thread_local uf::stl::vector<pod::Instance::Object> objects;
static thread_local uf::stl::vector<pod::Material> materials;
static thread_local uf::stl::vector<pod::Texture> textures;
static thread_local uf::stl::vector<pod::DrawCommand> drawCommands;
joints.clear();
for ( auto& key : storage.joints.keys ) { for ( auto& key : storage.joints.keys ) {
auto& matrices = storage.joints.map[key]; joints.insert( joints.end(), storage.joints.map[key].begin(), storage.joints.map[key].end() );
joints.reserve( joints.size() + matrices.size() );
for ( auto& mat : matrices ) joints.emplace_back( mat );
} }
rebuild = storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ) || rebuild; objects.clear();
rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild; for ( auto& key : storage.objects.keys ) {
rebuild = storage.buffers.joint.update( (const void*) joints.data(), joints.size() * sizeof(pod::Matrix4f) ) || rebuild; auto& entity = *storage.entities.map[key];
auto& object = storage.objects.map[key];
if ( entity.isValid() ) {
auto& metadata = entity.getComponent<uf::ObjectBehavior::Metadata>();
auto& transform = entity.getComponent<pod::Transform<>>();
if ( !metadata.system.ignoreGraph ) {
object.previous = object.model;
object.model = uf::transform::model( transform );
}
}
objects.emplace_back( object );
}
if ( !joints.empty() ) rebuild = storage.buffers.joint.update( (const void*) joints.data(), joints.size() * sizeof(pod::Matrix4f) ) || rebuild;
rebuild = storage.buffers.object.update( (const void*) objects.data(), objects.size() * sizeof(pod::Instance::Object) ) || rebuild; rebuild = storage.buffers.object.update( (const void*) objects.data(), objects.size() * sizeof(pod::Instance::Object) ) || rebuild;
if ( ::newGraphAdded ) { if ( ::newGraphAdded ) {
uf::stl::vector<pod::Material> materials = storage.materials.flatten(); instances.clear();
uf::stl::vector<pod::Texture> textures = storage.textures.flatten(); for ( auto& key : storage.primitives.keys ) {
uf::stl::vector<pod::DrawCommand> drawCommands; drawCommands.reserve(storage.drawCommands.map.size()); for ( auto& primitive : storage.primitives[key] ) {
instances.emplace_back( primitive.instance );
}
}
for ( auto& key : storage.drawCommands.keys ) drawCommands.insert( drawCommands.end(), storage.drawCommands.map[key].begin(), storage.drawCommands.map[key].end() ); instanceAddresses.clear();
for ( auto& key : storage.instanceAddresses.keys ) {
instanceAddresses.insert( instanceAddresses.end(), storage.instanceAddresses.map[key].begin(), storage.instanceAddresses.map[key].end() );
}
textures.clear();
for ( auto& key : storage.textures.keys ) textures.emplace_back( storage.textures.map[key] );
materials.clear();
for ( auto& key : storage.materials.keys ) materials.emplace_back( storage.materials.map[key] );
drawCommands.clear();
for ( auto& key : storage.primitives.keys ) {
for ( auto& primitive : storage.primitives[key] ) drawCommands.emplace_back( primitive.drawCommand );
}
rebuild = storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ) || rebuild;
rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild;
rebuild = storage.buffers.drawCommands.update( (const void*) drawCommands.data(), drawCommands.size() * sizeof(pod::DrawCommand) ) || rebuild; rebuild = storage.buffers.drawCommands.update( (const void*) drawCommands.data(), drawCommands.size() * sizeof(pod::DrawCommand) ) || rebuild;
rebuild = storage.buffers.material.update( (const void*) materials.data(), materials.size() * sizeof(pod::Material) ) || rebuild; rebuild = storage.buffers.material.update( (const void*) materials.data(), materials.size() * sizeof(pod::Material) ) || rebuild;
rebuild = storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) ) || rebuild; rebuild = storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) ) || rebuild;
@ -1482,6 +1460,7 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
::newGraphAdded = false; ::newGraphAdded = false;
} }
if ( rebuild ) { if ( rebuild ) {
UF_MSG_DEBUG("Graph buffers requesting renderer update"); UF_MSG_DEBUG("Graph buffers requesting renderer update");
uf::renderer::states::rebuild = true; uf::renderer::states::rebuild = true;
@ -1633,6 +1612,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
auto model = uf::transform::model( transform ); auto model = uf::transform::model( transform );
auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; auto& mesh = storage.meshes.map[graph.meshes[node.mesh]];
auto& primitives = storage.primitives.map[graph.primitives[node.mesh]]; auto& primitives = storage.primitives.map[graph.primitives[node.mesh]];
auto& instanceAddresses = storage.instanceAddresses.map[graph.primitives[node.mesh]];
float radius = graph.settings.stream.radius; float radius = graph.settings.stream.radius;
float radiusSquared = radius * radius; float radiusSquared = radius * radius;
@ -1932,10 +1912,12 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
// update buffers if any of them were resized (because my aliasing system is weak) // update buffers if any of them were resized (because my aliasing system is weak)
if ( rebuild ) { if ( rebuild ) {
::bindBuffers( graphic, mesh ); ::bindBuffers( graphic, mesh );
::bindInstanceAddresses( graphic, mesh, instanceAddresses );
uf::renderer::states::rebuild = true; uf::renderer::states::rebuild = true;
} }
} else { } else {
uf::graph::initializeGraphics( graph, entity, mesh ); uf::graph::initializeGraphics( graph, entity, mesh, instanceAddresses );
} }
} }
// bind mesh to physics state // bind mesh to physics state
@ -1996,8 +1978,10 @@ void uf::graph::update( pod::Graph& graph, float delta ) {
auto& graphic = entity.getComponent<uf::renderer::Graphic>(); auto& graphic = entity.getComponent<uf::renderer::Graphic>();
auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; auto& mesh = storage.meshes.map[graph.meshes[node.mesh]];
auto& instanceAddresses = storage.instanceAddresses.map[graph.primitives[node.mesh]];
::bindBuffers( graphic, mesh ); ::bindBuffers( graphic, mesh );
::bindInstanceAddresses( graphic, mesh, instanceAddresses );
} }
} }
@ -2009,50 +1993,5 @@ void uf::graph::update( pod::Graph& graph, float delta ) {
} }
#endif #endif
#if !UF_ENV_DREAMCAST
// update instance model
uf::stl::unordered_map<size_t, pod::Matrix4f> matrixCache;
for ( auto& node : graph.nodes ) {
if ( !( 0 <= node.object && node.object < storage.objects.keys.size() ) ) continue;
auto objectKeyName = ::objectKey( node );
auto& entity = *storage.entities[objectKeyName];
auto& object = storage.objects[objectKeyName];
if ( !entity.isValid() ) continue;
auto& metadata = entity.getComponent<uf::ObjectBehavior::Metadata>();
if ( metadata.system.ignoreGraph ) continue;
auto& transform = entity.getComponent<pod::Transform<>>();
object.previous = object.model;
object.model = uf::transform::model( transform );
}
/*
for ( auto& name : graph.primitives ) {
auto& primitives = storage.primitives[name];
for ( auto& primitive : primitives ) {
// auto& drawCommand = primitive.drawCommand;
auto& instance = primitive.instance;
instance.previous = instance.model;
if ( instanceCache.count( instance.objectID ) > 0 ) {
instance.model = instanceCache[instance.objectID];
continue;
}
auto& entity = *storage.entities[std::to_string(instance.objectID)];
if ( !entity.isValid() ) continue;
auto& metadata = entity.getComponent<uf::ObjectBehavior::Metadata>();
if ( metadata.system.ignoreGraph ) continue;
auto& transform = entity.getComponent<pod::Transform<>>();
instance.model = (instanceCache[instance.objectID] = uf::transform::model( transform ));
}
}
*/
#endif
uf::graph::updateAnimation( graph, delta ); uf::graph::updateAnimation( graph, delta );
} }

View File

@ -139,6 +139,8 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
if ( type == "bounding box" || type == "aabb" ) { if ( type == "bounding box" || type == "aabb" ) {
pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} ); pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} );
pod::Vector3f max = uf::vector::decode( metadataJsonPhysics["max"], pod::Vector3f{0.5f, 0.5f, 0.5f} ); pod::Vector3f max = uf::vector::decode( metadataJsonPhysics["max"], pod::Vector3f{0.5f, 0.5f, 0.5f} );
UF_MSG_DEBUG("entity={}, min={}, max={}", uf::string::toString( *this ), uf::vector::toString( min ), uf::vector::toString( max ));
#if UF_USE_REACTPHYSICS #if UF_USE_REACTPHYSICS
auto center = ( max + min ) * 0.5f; auto center = ( max + min ) * 0.5f;
if ( metadataJsonPhysics["recenter"].as<bool>(true) ) offset = (center - transform.position); if ( metadataJsonPhysics["recenter"].as<bool>(true) ) offset = (center - transform.position);

View File

@ -7,6 +7,9 @@ namespace binds {
pod::Vector3f& velocity( pod::PhysicsBody& self ) { return self.velocity; } pod::Vector3f& velocity( pod::PhysicsBody& self ) { return self.velocity; }
void setVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { self.velocity = v; } void setVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { self.velocity = v; }
void applyVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { self.velocity += v; } void applyVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { self.velocity += v; }
float getMass( const pod::PhysicsBody& self ) { return self.mass; }
void setMass( pod::PhysicsBody& self, float mass ) { self.mass = mass; }
void enableGravity( pod::PhysicsBody& state, bool s ) { void enableGravity( pod::PhysicsBody& state, bool s ) {
if ( !state.object ) return; if ( !state.object ) return;
@ -44,13 +47,10 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::PhysicsBody,
UF_LUA_REGISTER_USERTYPE_DEFINE( applyImpulse, UF_LUA_C_FUN(uf::physics::impl::applyImpulse) ), UF_LUA_REGISTER_USERTYPE_DEFINE( applyImpulse, UF_LUA_C_FUN(uf::physics::impl::applyImpulse) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( applyRotation, UF_LUA_C_FUN(::binds::applyRotation) ), UF_LUA_REGISTER_USERTYPE_DEFINE( applyRotation, UF_LUA_C_FUN(::binds::applyRotation) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( enableGravity, UF_LUA_C_FUN(::binds::enableGravity) ), UF_LUA_REGISTER_USERTYPE_DEFINE( enableGravity, UF_LUA_C_FUN(::binds::enableGravity) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( rayCast, UF_LUA_C_FUN(::binds::rayCast) ) UF_LUA_REGISTER_USERTYPE_DEFINE( rayCast, UF_LUA_C_FUN(::binds::rayCast) ),
// UF_LUA_REGISTER_USERTYPE_DEFINE( setImpulse, UF_LUA_C_FUN(uf::physics::impl::setImpulse) ),
// UF_LUA_REGISTER_USERTYPE_DEFINE( activateCollision, UF_LUA_C_FUN(uf::physics::impl::activateCollision) ),
// UF_LUA_REGISTER_USERTYPE_DEFINE( getMass, UF_LUA_C_FUN(uf::physics::impl::getMass) ), UF_LUA_REGISTER_USERTYPE_DEFINE( getMass, UF_LUA_C_FUN(::binds::getMass) ),
// UF_LUA_REGISTER_USERTYPE_DEFINE( setMass, UF_LUA_C_FUN(uf::physics::impl::setMass) ), UF_LUA_REGISTER_USERTYPE_DEFINE( setMass, UF_LUA_C_FUN(::binds::setMass) )
) )
#endif #endif

View File

@ -28,6 +28,20 @@ namespace {
const uf::stl::string DEFERRED_MODE = "compute"; const uf::stl::string DEFERRED_MODE = "compute";
ext::vulkan::Texture depthPyramid; ext::vulkan::Texture depthPyramid;
uf::stl::vector<VkImageView> depthPyramidViews; uf::stl::vector<VkImageView> depthPyramidViews;
void cmdImageBarrier(VkCommandBuffer commandBuffer, VkImage image, VkAccessFlags srcAccess, VkAccessFlags dstAccess, VkImageLayout oldLayout, VkImageLayout newLayout) {
VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &barrier);
};
} }
#include "./transition.inl" #include "./transition.inl"
@ -858,17 +872,18 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
::transitionAttachmentsTo( this, shader, commandBuffer ); ::transitionAttachmentsTo( this, shader, commandBuffer );
// dispatch compute shader // dispatch compute shader
VkMemoryBarrier memoryBarrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER}; auto& attachmentColor = this->getAttachment("color"); // color
memoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; auto& attachmentBright = this->getAttachment("bright"); // bloom
memoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; auto& attachmentScratch = this->getAttachment("scratch"); // pingpong
vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_FLAGS_NONE, 1, &memoryBarrier, 0, NULL, 0, NULL );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[1]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[1]" );
blitter.record(commandBuffer, descriptor, 0, 1); blitter.record(commandBuffer, descriptor, 0, 1);
vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_FLAGS_NONE, 1, &memoryBarrier, 0, NULL, 0, NULL ); cmdImageBarrier( commandBuffer, attachmentScratch.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[2]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[2]" );
blitter.record(commandBuffer, descriptor, 0, 2); blitter.record(commandBuffer, descriptor, 0, 2);
vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_FLAGS_NONE, 1, &memoryBarrier, 0, NULL, 0, NULL ); cmdImageBarrier( commandBuffer, attachmentBright.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[3]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[3]" );
blitter.record(commandBuffer, descriptor, 0, 3); blitter.record(commandBuffer, descriptor, 0, 3);

View File

@ -453,7 +453,7 @@ namespace {
const auto& nodeA = bvh.nodes[nodeAID]; const auto& nodeA = bvh.nodes[nodeAID];
const auto& nodeB = bvh.nodes[nodeBID]; const auto& nodeB = bvh.nodes[nodeBID];
if ( nodeA.isAsleep() || nodeB.isAsleep() || !::aabbOverlap( bvh.bounds[nodeAID], bvh.bounds[nodeBID] ) ) return; if ( (nodeA.isAsleep() && nodeB.isAsleep()) || !::aabbOverlap( bvh.bounds[nodeAID], bvh.bounds[nodeBID] ) ) return;
if ( nodeA.getCount() > 0 && nodeB.getCount() > 0 ) { if ( nodeA.getCount() > 0 && nodeB.getCount() > 0 ) {
for ( auto i = 0; i < nodeA.getCount(); ++i ) { for ( auto i = 0; i < nodeA.getCount(); ++i ) {
@ -483,7 +483,7 @@ namespace {
const auto& nodeA = bvhA.nodes[nodeAID]; const auto& nodeA = bvhA.nodes[nodeAID];
const auto& nodeB = bvhB.nodes[nodeBID]; const auto& nodeB = bvhB.nodes[nodeBID];
if ( nodeA.isAsleep() || nodeB.isAsleep() || !::aabbOverlap( bvhA.bounds[nodeAID], bvhB.bounds[nodeBID] ) ) return; if ( (nodeA.isAsleep() && nodeB.isAsleep()) || !::aabbOverlap( bvhA.bounds[nodeAID], bvhB.bounds[nodeBID] ) ) return;
if ( nodeA.getCount() > 0 && nodeB.getCount() > 0 ) { if ( nodeA.getCount() > 0 && nodeB.getCount() > 0 ) {
for ( auto i = 0; i < nodeA.getCount(); ++i ) { for ( auto i = 0; i < nodeA.getCount(); ++i ) {
@ -556,7 +556,7 @@ namespace {
outIndices.reserve(::reserveCount); outIndices.reserve(::reserveCount);
thread_local uf::stl::stack<pod::BVH::index_t> stack; static thread_local uf::stl::stack<pod::BVH::index_t> stack;
//stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function //stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function
stack.push(0); stack.push(0);
@ -603,7 +603,7 @@ namespace {
if ( bvh.nodes.empty() ) return; if ( bvh.nodes.empty() ) return;
outIndices.reserve(::reserveCount); outIndices.reserve(::reserveCount);
thread_local uf::stl::stack<pod::BVH::index_t> stack; static thread_local uf::stl::stack<pod::BVH::index_t> stack;
//stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function //stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function
stack.push(0); stack.push(0);

View File

@ -3,7 +3,6 @@
#include <uf/engine/scene/scene.h> #include <uf/engine/scene/scene.h>
#include <uf/utils/mesh/mesh.h> #include <uf/utils/mesh/mesh.h>
#include <uf/utils/memory/stack.h> #include <uf/utils/memory/stack.h>
#include <mutex>
namespace { namespace {
bool warmupSolver = true; // cache manifold data to warm up the solver bool warmupSolver = true; // cache manifold data to warm up the solver
@ -33,7 +32,6 @@ namespace {
float baumgarteCorrectionPercent = 0.4f; float baumgarteCorrectionPercent = 0.4f;
float baumgarteCorrectionSlop = 0.01f; float baumgarteCorrectionSlop = 0.01f;
std::mutex cacheMutex;
uf::stl::unordered_map<size_t, pod::Manifold> manifoldsCache; uf::stl::unordered_map<size_t, pod::Manifold> manifoldsCache;
uint32_t manifoldCacheLifetime = 6; // to-do: find a good value for this uint32_t manifoldCacheLifetime = 6; // to-do: find a good value for this
@ -167,6 +165,8 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
uf::stl::vector<pod::Island> islands; uf::stl::vector<pod::Island> islands;
::buildIslands( pairs, bodies, islands ); ::buildIslands( pairs, bodies, islands );
if ( ::warmupSolver ) ::prepareManifoldCache( ::manifoldsCache, islands, bodies );
// iterate islands // iterate islands
#pragma omp parallel for schedule(dynamic) #pragma omp parallel for schedule(dynamic)
for ( auto& island : islands ) { for ( auto& island : islands ) {
@ -190,7 +190,10 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
for ( auto& c : manifold.points ) c.normal = ::orientNormalToAB( a, b, c.normal ); for ( auto& c : manifold.points ) c.normal = ::orientNormalToAB( a, b, c.normal );
} }
// retrieve accumulated impulses // retrieve accumulated impulses
if ( ::warmupSolver ) ::retrieveContacts( manifold, ::manifoldsCache[::makePairKey( a, b )] ); if ( ::warmupSolver ) {
auto it = ::manifoldsCache.find( ::makePairKey( a, b ) );
if ( it != ::manifoldsCache.end() ) ::retrieveContacts( manifold, it->second );
}
// merge similar contacts from a mesh to ensure continuity // merge similar contacts from a mesh to ensure continuity
if ( a.collider.type == pod::ShapeType::MESH || b.collider.type == pod::ShapeType::MESH ) { if ( a.collider.type == pod::ShapeType::MESH || b.collider.type == pod::ShapeType::MESH ) {
::mergeContacts( manifold ); ::mergeContacts( manifold );
@ -221,11 +224,12 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
::solvePositions( manifolds, dt ); ::solvePositions( manifolds, dt );
// cache manifold positions // cache manifold positions
if ( ::warmupSolver ) { if ( ::warmupSolver ) {
std::lock_guard<std::mutex> lock(::cacheMutex); ::updateManifoldCache( manifolds, ::manifoldsCache );
::storeManifolds( manifolds, ::manifoldsCache );
} }
} }
if ( ::warmupSolver ) ::pruneManifoldCache( ::manifoldsCache );
for ( auto* b : bodies ) { for ( auto* b : bodies ) {
if ( b->isStatic ) continue; if ( b->isStatic ) continue;
::snapVelocity( *b, dt ); ::snapVelocity( *b, dt );
@ -294,18 +298,8 @@ void uf::physics::impl::updateInertia( pod::PhysicsBody& body ) {
*/ */
pod::Vector3f dims = (body.collider.aabb.max - body.collider.aabb.min); pod::Vector3f dims = (body.collider.aabb.max - body.collider.aabb.min);
pod::Vector3f dimsSq = dims * dims; pod::Vector3f dimsSq = dims * dims;
body.inertiaTensor = pod::Vector3f{ dimsSq.y + dimsSq.z, dimsSq.x + dimsSq.z, dimsSq.x + dimsSq.y } * (body.mass / 12.0f);
float massFactor = body.mass / 12.0f; body.inertiaTensor = uf::vector::max( body.inertiaTensor, { EPS(1.0e-6f), EPS(1.0e-6f), EPS(1.0e-6f) } );
body.inertiaTensor = {
massFactor * (dimsSq.y + dimsSq.z),
massFactor * (dimsSq.x + dimsSq.z),
massFactor * (dimsSq.x + dimsSq.y),
};
body.inertiaTensor.x = std::max(body.inertiaTensor.x, EPS(1e-6f));
body.inertiaTensor.y = std::max(body.inertiaTensor.y, EPS(1e-6f));
body.inertiaTensor.z = std::max(body.inertiaTensor.z, EPS(1e-6f));
body.inverseInertiaTensor = 1.0f / body.inertiaTensor; body.inverseInertiaTensor = 1.0f / body.inertiaTensor;
} break; } break;
case pod::ShapeType::SPHERE: { case pod::ShapeType::SPHERE: {
@ -341,37 +335,42 @@ void uf::physics::impl::updateInertia( pod::PhysicsBody& body ) {
totalVolume += extents.x * extents.y * extents.z; totalVolume += extents.x * extents.y * extents.z;
} }
// accumulate inertia if ( totalVolume < EPS(1.0e-6) ) {
for ( size_t i = 0; i < bvh.nodes.size(); ++i ) { body.inertiaTensor = { FLT_MAX, FLT_MAX, FLT_MAX };
if ( bvh.nodes[i].getCount() == 0 ) continue; body.inverseInertiaTensor = { 0.0f, 0.0f, 0.0f };
const auto& box = bvh.bounds[i]; } else {
// accumulate inertia
for ( size_t i = 0; i < bvh.nodes.size(); ++i ) {
if ( bvh.nodes[i].getCount() == 0 ) continue;
const auto& box = bvh.bounds[i];
auto extents = box.max - box.min; auto extents = box.max - box.min;
float mass = body.mass * extents.x * extents.y * extents.z / totalVolume; float mass = body.mass * extents.x * extents.y * extents.z / totalVolume;
// inertia tensor of a box about its center // inertia tensor of a box about its center
float x2 = extents.x * extents.x; float x2 = extents.x * extents.x;
float y2 = extents.y * extents.y; float y2 = extents.y * extents.y;
float z2 = extents.z * extents.z; float z2 = extents.z * extents.z;
pod::Matrix3f Ibox; pod::Matrix3f Ibox;
Ibox(0,0) = (1.0f/12.0f) * mass * (y2 + z2); Ibox(0,0) = (1.0f/12.0f) * mass * (y2 + z2);
Ibox(1,1) = (1.0f/12.0f) * mass * (x2 + z2); Ibox(1,1) = (1.0f/12.0f) * mass * (x2 + z2);
Ibox(2,2) = (1.0f/12.0f) * mass * (x2 + y2); Ibox(2,2) = (1.0f/12.0f) * mass * (x2 + y2);
// parallel axis theorem // parallel axis theorem
pod::Vector3f center = (box.min + box.max) * 0.5f; pod::Vector3f center = (box.min + box.max) * 0.5f;
pod::Vector3f d = center; // relative to mesh COM (assume COM at origin for now) pod::Vector3f d = center; // relative to mesh COM (assume COM at origin for now)
float dist2 = uf::vector::magnitude( d ); float dist2 = uf::vector::magnitude( d );
pod::Matrix3f pat = uf::matrix::identityi<pod::Matrix3f>() * (mass * dist2); pod::Matrix3f pat = uf::matrix::identityi<pod::Matrix3f>() * (mass * dist2);
pat -= uf::matrix::outerProduct(d, d) * mass; pat -= uf::matrix::outerProduct(d, d) * mass;
inertia += Ibox + pat; inertia += Ibox + pat;
}
body.inertiaTensor = { inertia(0,0), inertia(1,1), inertia(2,2) };
body.inverseInertiaTensor = 1.0f / body.inertiaTensor;
} }
body.inertiaTensor = { inertia(0,0), inertia(1,1), inertia(2,2) };
body.inverseInertiaTensor = 1.0f / body.inertiaTensor;
} break; } break;
// to-do: add others // to-do: add others
default: { default: {
@ -560,7 +559,7 @@ pod::RayQuery uf::physics::impl::rayCast( const pod::Ray& ray, const pod::World&
auto& staticBvh = world.staticBvh; auto& staticBvh = world.staticBvh;
auto& bodies = world.bodies; auto& bodies = world.bodies;
thread_local uf::stl::vector<pod::BVH::index_t> candidates; static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
candidates.clear(); candidates.clear();
::queryBVH( dynamicBvh, ray, candidates ); ::queryBVH( dynamicBvh, ray, candidates );
if ( ::useSplitBvhs ) ::queryBVH( staticBvh, ray, candidates ); if ( ::useSplitBvhs ) ::queryBVH( staticBvh, ray, candidates );

View File

@ -175,24 +175,37 @@ namespace {
} }
} }
void storeManifolds( uf::stl::vector<pod::Manifold>& manifolds, uf::stl::unordered_map<size_t, pod::Manifold>& manifoldsCache ){ void prepareManifoldCache( uf::stl::unordered_map<size_t, pod::Manifold>& cache, const uf::stl::vector<pod::Island>& islands, const uf::stl::vector<pod::PhysicsBody*>& bodies ) {
// update cache for ( const auto& island : islands ) {
for ( auto& manifold : manifolds ) { for ( const auto& pair : island.pairs ) {
manifoldsCache[::makePairKey( *manifold.a, *manifold.b )] = manifold; auto& a = *bodies[pair.first];
} auto& b = *bodies[pair.second];
// prune if too old / empty cache[ ::makePairKey( a, b ) ];
for ( auto itCache = manifoldsCache.begin(); itCache != manifoldsCache.end(); ) { }
}
}
void updateManifoldCache( const uf::stl::vector<pod::Manifold>& manifolds, uf::stl::unordered_map<size_t, pod::Manifold>& cache ) {
for ( const auto& m : manifolds ) {
auto it = cache.find( ::makePairKey( *m.a, *m.b ) );
if ( it == cache.end() ) continue; // assert
it->second = m;
}
}
void pruneManifoldCache( uf::stl::unordered_map<size_t, pod::Manifold>& cache ) {
for ( auto itCache = cache.begin(); itCache != cache.end(); ) {
auto& manifold = itCache->second; auto& manifold = itCache->second;
// prune manifolds that are X frames old // prune points that are too old
for ( auto it = manifold.points.begin(); it != manifold.points.end(); ) { for ( auto it = manifold.points.begin(); it != manifold.points.end(); ) {
if ( it->lifetime > ::manifoldCacheLifetime ) it = manifold.points.erase(it); if ( it->lifetime > ::manifoldCacheLifetime ) it = manifold.points.erase(it);
else ++it; else ++it;
} }
// empty manifold, kill it // empty manifold, kill it
if ( manifold.points.empty() ) itCache = manifoldsCache.erase(itCache); if ( manifold.points.empty() ) itCache = cache.erase(itCache);
else ++itCache; else ++itCache;
} }
} }

View File

@ -24,7 +24,7 @@ namespace {
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( aabb.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( aabb.bounds, ::getTransform( mesh ) );
thread_local uf::stl::vector<pod::BVH::index_t> candidates; static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
candidates.clear(); candidates.clear();
::queryBVH( bvh, bounds, candidates ); ::queryBVH( bvh, bounds, candidates );
@ -48,7 +48,7 @@ namespace {
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( sphere.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( sphere.bounds, ::getTransform( mesh ) );
thread_local uf::stl::vector<pod::BVH::index_t> candidates; static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
candidates.clear(); candidates.clear();
::queryBVH( bvh, bounds, candidates ); ::queryBVH( bvh, bounds, candidates );
@ -74,7 +74,7 @@ namespace {
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( plane.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( plane.bounds, ::getTransform( mesh ) );
thread_local uf::stl::vector<pod::BVH::index_t> candidates; static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
candidates.clear(); candidates.clear();
::queryBVH( bvh, bounds, candidates ); ::queryBVH( bvh, bounds, candidates );
@ -99,7 +99,7 @@ namespace {
// transform to local space for BVH query // transform to local space for BVH query
auto bounds = ::transformAabbToLocal( capsule.bounds, ::getTransform( mesh ) ); auto bounds = ::transformAabbToLocal( capsule.bounds, ::getTransform( mesh ) );
thread_local uf::stl::vector<pod::BVH::index_t> candidates; static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
candidates.clear(); candidates.clear();
::queryBVH( bvh, bounds, candidates ); ::queryBVH( bvh, bounds, candidates );
@ -124,13 +124,13 @@ namespace {
const auto& meshB = *b.collider.mesh.mesh; const auto& meshB = *b.collider.mesh.mesh;
// compute overlaps between one BVH and another BVH // compute overlaps between one BVH and another BVH
thread_local pod::BVH::pairs_t pairs; static thread_local pod::BVH::pairs_t pairs;
pairs.clear(); pairs.clear();
::queryOverlaps( bvhA, bvhB, pairs ); ::queryOverlaps( bvhA, bvhB, pairs );
bool hit = false; bool hit = false;
// do collision per triangle // do collision per triangle
for (auto [ idA, idB] : pairs ) { for (auto [idA, idB] : pairs ) {
auto tA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space auto tA = ::fetchTriangle( meshA, idA, a ); // transform triangles to world space
auto tB = ::fetchTriangle( meshB, idB, b ); auto tB = ::fetchTriangle( meshB, idB, b );

View File

@ -144,12 +144,12 @@ namespace {
float penetrationBias = std::max(contact.penetration - ::baumgarteCorrectionSlop, 0.0f) * (::baumgarteCorrectionPercent / dt); float penetrationBias = std::max(contact.penetration - ::baumgarteCorrectionSlop, 0.0f) * (::baumgarteCorrectionPercent / dt);
penetrationBias = std::min(penetrationBias, 2.0f / dt); // clamp penetrationBias = std::min(penetrationBias, 2.0f / dt); // clamp
float maxPenetrationRecovery = 2.0f; // Limit to 2 units per second float maxPenetrationRecovery = 2.0f; // limit to 2 units per second
if ( penetrationBias > maxPenetrationRecovery ) penetrationBias = maxPenetrationRecovery; if ( penetrationBias > maxPenetrationRecovery ) penetrationBias = maxPenetrationRecovery;
float cDot = vRel + penetrationBias; float cDot = vRel + penetrationBias;
rhs[i] = (cDot < 0.0f) ? -cDot : 0.0f; // rHS is magnitude of correction needed rhs[i] = (cDot < 0.0f) ? -cDot : 0.0f; // RHS is magnitude of correction needed
lambda[i] = contact.accumulatedNormalImpulse; lambda[i] = contact.accumulatedNormalImpulse;
} }

View File

@ -303,6 +303,7 @@ namespace {
float planeDist = uf::vector::dot(triNormal, v0); float planeDist = uf::vector::dot(triNormal, v0);
if ( uf::vector::dot(bestAxis, triNormal) < 0.0f ) bestAxis = -bestAxis; if ( uf::vector::dot(bestAxis, triNormal) < 0.0f ) bestAxis = -bestAxis;
pod::Vector3f contact = boxCenter - bestAxis * (boxHalf.x * fabs(bestAxis.x) + boxHalf.y * fabs(bestAxis.y) + boxHalf.z * fabs(bestAxis.z)); pod::Vector3f contact = boxCenter - bestAxis * (boxHalf.x * fabs(bestAxis.x) + boxHalf.y * fabs(bestAxis.y) + boxHalf.z * fabs(bestAxis.z));
//pod::Vector3f contact = boxCenter - triNormal * planeDist; //pod::Vector3f contact = boxCenter - triNormal * planeDist;
/* /*