reverted hook hashes because all non-entity hook dispatches breaks, dilate texture atlases if padding is requested, proper mipmapping in deferred shader, some other code cleanup, parse mass from MDL, load error model as fallback
This commit is contained in:
parent
f9d7cf2dc8
commit
5931a5dc6f
@ -1,10 +1,10 @@
|
||||
{
|
||||
"engine": {
|
||||
"scenes": {
|
||||
"start": "SourceEngine",
|
||||
"start": "StartMenu",
|
||||
"matrix": { "reverseInfinite": true },
|
||||
"lights": { "enabled": true,
|
||||
"lightmaps": false,
|
||||
"lightmaps": true,
|
||||
"max": 32,
|
||||
"shadows": {
|
||||
"enabled": false,
|
||||
@ -87,7 +87,7 @@
|
||||
]
|
||||
},
|
||||
"framebuffer": {
|
||||
"msaa": 1,
|
||||
"msaa": 8,
|
||||
"size": 1
|
||||
// "size": [ 640, 480, "NEAREST" ]
|
||||
// "size": [ 1280, 720 ]
|
||||
@ -109,7 +109,7 @@
|
||||
"invariant": {
|
||||
"default stage buffers": true,
|
||||
"default defer buffer destroy": true,
|
||||
"default command buffer immediate": true,
|
||||
"default command buffer immediate": false,
|
||||
"n-buffered uniform": false,
|
||||
"multithreaded recording": true
|
||||
},
|
||||
@ -359,7 +359,7 @@
|
||||
},
|
||||
"debug draw": {
|
||||
"static": false,
|
||||
"dynamic": false,
|
||||
"dynamic": true,
|
||||
"trigger": false,
|
||||
"contacts": false,
|
||||
"constraints": true,
|
||||
|
||||
@ -32,13 +32,10 @@
|
||||
// automatically handled
|
||||
// "/^func_door/": { "action": "load", "payload": { "import": "/door.json" } },
|
||||
// "/^prop_door/": { "action": "load", "payload": { "import": "/door.json" } },
|
||||
|
||||
|
||||
// regexp matches
|
||||
"/^prop_static/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
"/^prop_dynamic/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
"/^func_physbox/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
"/^prop_physics/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
// "/^prop_static/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
// "/^prop_dynamic/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
// "/^func_physbox/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
// "/^prop_physics/": { "action": "load", "payload": { "import": "ent://prop.json" } },
|
||||
|
||||
"/^tools\\/toolsnodraw/": { "material": { "base": [ 1.0, 1.0, 1.0, 0.0 ] } }
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
// "import": "./rp_downtown_v2.json"
|
||||
// "import": "./ss2_medsci1.json"
|
||||
"import": "./mds_mcdonalds.json"
|
||||
// "import": "./cs_office.json"
|
||||
// "import": "./mds_mcdonalds.json"
|
||||
"import": "./cs_office.json"
|
||||
// "import": "./gm_construct.json"
|
||||
}
|
||||
@ -192,6 +192,13 @@ bool validTextureIndex( uint start, int offset ) {
|
||||
uint textureIndex( uint start, int offset ) {
|
||||
return start + offset;
|
||||
}
|
||||
vec4 sampleTexture( uint id, vec2 uv, vec2 ddx, vec2 ddy ) {
|
||||
const Texture t = textures[id];
|
||||
vec2 scale = t.lerp.zw - t.lerp.xy;
|
||||
vec2 final_uv = mix( t.lerp.xy, t.lerp.zw, uv );
|
||||
|
||||
return textureGrad( samplerTextures[nonuniformEXT(t.index)], final_uv, ddx * scale, ddy * scale );
|
||||
}
|
||||
vec4 sampleTexture( uint id, vec2 uv ) {
|
||||
const Texture t = textures[id];
|
||||
return texture( samplerTextures[nonuniformEXT(t.index)], mix( t.lerp.xy, t.lerp.zw, uv ) );
|
||||
@ -205,7 +212,14 @@ vec4 sampleTexture( uint id, vec2 uv, float mip ) {
|
||||
#endif
|
||||
}
|
||||
vec4 sampleTexture( uint id, vec3 uvm ) { return sampleTexture( id, uvm.xy, uvm.z ); }
|
||||
vec4 sampleTexture( uint id ) { return sampleTexture( id, surface.uv.xy, surface.uv.z ); }
|
||||
vec4 sampleTexture( uint id ) {
|
||||
#if QUERY_MIPMAP
|
||||
return sampleTexture( id, uv );
|
||||
#else
|
||||
return sampleTexture( id, surface.uv.xy, surface.dUvDx, surface.dUvDy );
|
||||
#endif
|
||||
}
|
||||
// vec4 sampleTexture( uint id ) { return sampleTexture( id, surface.uv.xy, surface.uv.z ); }
|
||||
vec4 sampleTexture( uint id, float mip ) { return sampleTexture( id, surface.uv.xy, mip ); }
|
||||
#endif
|
||||
vec2 rayBoxDst( vec3 boundsMin, vec3 boundsMax, in Ray ray ) {
|
||||
@ -323,7 +337,7 @@ void populateSurfaceMaterial() {
|
||||
// Light mapping
|
||||
if ( ( bool(ubo.settings.lighting.useLightmaps)) && validTextureIndex( surface.instance.lightmapID ) ) {
|
||||
surface.material.lightmapped = true; // light.a > 0.001;
|
||||
vec4 light = decodeRGBE( sampleTexture( surface.instance.lightmapID, surface.st.xy ) );
|
||||
vec4 light = decodeRGBE( sampleTexture( surface.instance.lightmapID, surface.st.xy, 0.0 ) );
|
||||
|
||||
const vec3 F0 = mix(vec3(0.04), surface.material.albedo.rgb, surface.material.metallic);
|
||||
const vec3 Lo = normalize(-surface.position.eye);
|
||||
@ -355,53 +369,45 @@ uvec4 uvec2_16x4( uvec2 i ) {
|
||||
}
|
||||
|
||||
#if BUFFER_REFERENCE
|
||||
|
||||
void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
|
||||
Triangle triangle;
|
||||
Vertex points[3];
|
||||
if ( false && isValidAddress(addresses.vertex) ) {
|
||||
// Vertices vertices = Vertices(nonuniformEXT(addresses.vertex));
|
||||
// #pragma unroll 3
|
||||
// for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_] = vertices.v[/*triangle.*/indices[_]];
|
||||
if ( isValidAddress(addresses.position) ) {
|
||||
VPos buf = VPos(nonuniformEXT(addresses.position));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].position = vec3( buf.v[indices[_]*3+0], buf.v[indices[_]*3+1], buf.v[indices[_]*3+2] );
|
||||
}
|
||||
if ( isValidAddress(addresses.uv) ) {
|
||||
VUv buf = VUv(nonuniformEXT(addresses.uv));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].uv = buf.v[indices[_]];
|
||||
}
|
||||
if ( isValidAddress(addresses.st) ) {
|
||||
VSt buf = VSt(nonuniformEXT(addresses.st));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].st = buf.v[indices[_]];
|
||||
}
|
||||
if ( isValidAddress(addresses.normal) ) {
|
||||
VNormal buf = VNormal(nonuniformEXT(addresses.normal));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].normal = vec3( buf.v[indices[_]*3+0], buf.v[indices[_]*3+1], buf.v[indices[_]*3+2] );
|
||||
}
|
||||
if ( isValidAddress(addresses.tangent) ) {
|
||||
VTangent buf = VTangent(nonuniformEXT(addresses.tangent));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = vec3( buf.v[indices[_]*3+0], buf.v[indices[_]*3+1], buf.v[indices[_]*3+2] );
|
||||
} else {
|
||||
if ( isValidAddress(addresses.position) ) {
|
||||
VPos buf = VPos(nonuniformEXT(addresses.position));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].position = vec3( buf.v[indices[_]*3+0], buf.v[indices[_]*3+1], buf.v[indices[_]*3+2] );
|
||||
//for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].position[_] = buf.v[/*triangle.*/indices[_]*3+_];
|
||||
}
|
||||
if ( isValidAddress(addresses.uv) ) {
|
||||
VUv buf = VUv(nonuniformEXT(addresses.uv));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].uv/*[_]*/ = buf.v[/*triangle.*/indices[_]];
|
||||
}
|
||||
if ( isValidAddress(addresses.st) ) {
|
||||
VSt buf = VSt(nonuniformEXT(addresses.st));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].st/*[_]*/ = buf.v[/*triangle.*/indices[_]];
|
||||
}
|
||||
if ( isValidAddress(addresses.normal) ) {
|
||||
VNormal buf = VNormal(nonuniformEXT(addresses.normal));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].normal = vec3( buf.v[indices[_]*3+0], buf.v[indices[_]*3+1], buf.v[indices[_]*3+2] );
|
||||
// for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].normal[_] = buf.v[/*triangle.*/indices[_]*3+_];
|
||||
}
|
||||
if ( isValidAddress(addresses.tangent) ) {
|
||||
VTangent buf = VTangent(nonuniformEXT(addresses.tangent));
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = vec3( buf.v[indices[_]*3+0], buf.v[indices[_]*3+1], buf.v[indices[_]*3+2] );
|
||||
// for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].tangent[_] = buf.v[/*triangle.*/indices[_]*3+_];
|
||||
} else {
|
||||
vec3 edge1 = points[1].position - points[0].position;
|
||||
vec3 edge2 = points[2].position - points[0].position;
|
||||
vec2 deltaUV1 = points[1].uv - points[0].uv;
|
||||
vec2 deltaUV2 = points[2].uv - points[0].uv;
|
||||
vec3 edge1 = points[1].position - points[0].position;
|
||||
vec3 edge2 = points[2].position - points[0].position;
|
||||
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);
|
||||
vec3 tangent_tri = (edge1 * deltaUV2.y - edge2 * deltaUV1.y) * r;
|
||||
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
|
||||
vec3 tangent_tri = (edge1 * deltaUV2.y - edge2 * deltaUV1.y) * r;
|
||||
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = tangent_tri;
|
||||
}
|
||||
#pragma unroll 3
|
||||
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = tangent_tri;
|
||||
}
|
||||
|
||||
#if BARYCENTRIC_CALCULATE
|
||||
@ -464,6 +470,115 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
|
||||
surface.st.xy = triangle.point.st;
|
||||
surface.st.z = 0;
|
||||
}
|
||||
|
||||
// bind UV derivatives
|
||||
{
|
||||
surface.dUvDx = vec2(0.0);
|
||||
surface.dUvDy = vec2(0.0);
|
||||
|
||||
#if BARYCENTRIC && MULTISAMPLING
|
||||
ivec2 size = textureSize(samplerId).xy;
|
||||
int sampleIdx = msaa.currentID;
|
||||
#elif BARYCENTRIC
|
||||
ivec2 size = textureSize(samplerId, 0).xy;
|
||||
int sampleIdx = 0;
|
||||
#else
|
||||
ivec2 size = imageSize(outImage).xy;
|
||||
int sampleIdx = 0;
|
||||
#endif
|
||||
|
||||
#if BARYCENTRIC
|
||||
ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
|
||||
int layer = int(gl_GlobalInvocationID.z);
|
||||
|
||||
uvec2 centerID = uvec2(texelFetch(samplerId, ivec3(coord, layer), sampleIdx).xy);
|
||||
|
||||
#if BARYCENTRIC_CALCULATE
|
||||
#if USE_CAMERA_VIEWPORT
|
||||
mat4 iProj = inverse( camera.viewport[surface.pass].projection );
|
||||
mat4 iView = inverse( camera.viewport[surface.pass].view );
|
||||
#else
|
||||
mat4 iProj = ubo.eyes[surface.pass].iProjection;
|
||||
mat4 iView = ubo.eyes[surface.pass].iView;
|
||||
#endif
|
||||
mat4 invModel = inverse(surface.object.model);
|
||||
|
||||
vec3 pA = points[0].position;
|
||||
vec3 v0 = points[1].position - pA;
|
||||
vec3 v1 = points[2].position - pA;
|
||||
float d00 = dot(v0, v0);
|
||||
float d01 = dot(v0, v1);
|
||||
float d11 = dot(v1, v1);
|
||||
float denom = d00 * d11 - d01 * d01;
|
||||
#endif
|
||||
|
||||
ivec2 offsetX[2] = ivec2[]( ivec2(1, 0), ivec2(-1, 0) );
|
||||
ivec2 offsetY[2] = ivec2[]( ivec2(0, 1), ivec2(0, -1) );
|
||||
|
||||
#if !BARYCENTRIC_CALCULATE
|
||||
#define FETCH_NEIGHBOR_UV(OFFSETS, OUT_GRAD) \
|
||||
for (int i = 0; i < 2; ++i) { \
|
||||
ivec2 off = OFFSETS[i]; \
|
||||
ivec2 nCoord = coord + off; \
|
||||
if ( nCoord.x >= 0 && nCoord.y >= 0 && nCoord.x < size.x && nCoord.y < size.y ) { \
|
||||
if ( uvec2(texelFetch(samplerId, ivec3(nCoord, layer), sampleIdx).xy) == centerID ) { \
|
||||
vec3 bN = decodeBarycentrics(texelFetch(samplerBary, ivec3(nCoord, layer), sampleIdx).xy); \
|
||||
vec2 uvN = points[0].uv * bN.x + points[1].uv * bN.y + points[2].uv * bN.z; \
|
||||
OUT_GRAD = (uvN - surface.uv.xy) * float(off.x + off.y); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define FETCH_NEIGHBOR_UV( OFFSETS, OUT_GRAD ) \
|
||||
for (int i = 0; i < 2; ++i) { \
|
||||
ivec2 off = OFFSETS[i]; \
|
||||
ivec2 nCoord = coord + off; \
|
||||
if ( nCoord.x >= 0 && nCoord.y >= 0 && nCoord.x < size.x && nCoord.y < size.y ) { \
|
||||
if ( uvec2(texelFetch(samplerId, ivec3(nCoord, layer), sampleIdx).xy) == centerID ) { \
|
||||
float dN = texelFetch(samplerDepth, ivec3(nCoord, layer), sampleIdx).r; \
|
||||
vec2 inUvN = (vec2(nCoord) / vec2(size)) * 2.0f - 1.0f; \
|
||||
vec4 eyeN = iProj * vec4(inUvN, dN, 1.0); \
|
||||
vec3 pN = vec3(invModel * vec4(vec3(iView * (eyeN / eyeN.w)), 1.0)); \
|
||||
vec3 v2N = pN - pA; \
|
||||
float vN = (d11 * dot(v2N, v0) - d01 * dot(v2N, v1)) / denom; \
|
||||
float wN = (d00 * dot(v2N, v1) - d01 * dot(v2N, v0)) / denom; \
|
||||
vec2 uvN = points[0].uv * (1.0f - vN - wN) + points[1].uv * vN + points[2].uv * wN; \
|
||||
OUT_GRAD = (uvN - surface.uv.xy) * float(off.x + off.y); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
FETCH_NEIGHBOR_UV( offsetX, surface.dUvDx );
|
||||
FETCH_NEIGHBOR_UV( offsetY, surface.dUvDy );
|
||||
|
||||
#undef FETCH_NEIGHBOR_UV
|
||||
#endif
|
||||
if ( surface.dUvDx == vec2(0.0) && surface.dUvDy == vec2(0.0) ) {
|
||||
#if USE_CAMERA_VIEWPORT
|
||||
mat4 proj = camera.viewport[surface.pass].projection;
|
||||
#else
|
||||
mat4 proj = ubo.eyes[surface.pass].projection;
|
||||
#endif
|
||||
|
||||
float pixelSize = abs(surface.position.eye.z) * 2.0 / (proj[1][1] * float(size.y));
|
||||
|
||||
vec3 e0 = points[1].position - points[0].position;
|
||||
vec3 e1 = points[2].position - points[0].position;
|
||||
float geomArea = length(cross(e0, e1));
|
||||
|
||||
vec2 dUv1 = points[1].uv - points[0].uv;
|
||||
vec2 dUv2 = points[2].uv - points[0].uv;
|
||||
float uvArea = abs(dUv1.x * dUv2.y - dUv2.x * dUv1.y);
|
||||
float uvPerMeter = sqrt(uvArea / max(geomArea, 0.00001));
|
||||
float fallback = pixelSize * uvPerMeter;
|
||||
|
||||
surface.dUvDx = vec2(fallback, 0.0);
|
||||
surface.dUvDy = vec2(0.0, fallback);
|
||||
}
|
||||
}
|
||||
|
||||
populateSurfaceMaterial();
|
||||
}
|
||||
|
||||
@ -175,6 +175,8 @@ struct Surface {
|
||||
mat3 tbn;
|
||||
vec3 barycentric;
|
||||
vec2 motion;
|
||||
vec2 dUvDx;
|
||||
vec2 dUvDy;
|
||||
|
||||
Ray ray;
|
||||
|
||||
|
||||
@ -145,11 +145,13 @@ layout (binding = 21, set = 0) uniform sampler3D samplerNoise;
|
||||
bool USE_SKYBOX_ON_DIVERGENCE = false;
|
||||
|
||||
void postProcess() {
|
||||
#if !MULTISAMPLING
|
||||
if ( USE_SKYBOX_ON_DIVERGENCE ) {
|
||||
if ( 0 <= ubo.settings.lighting.indexSkybox && ubo.settings.lighting.indexSkybox < CUBEMAPS ) {
|
||||
surface.fragment.rgb = texture( samplerCubemaps[ubo.settings.lighting.indexSkybox], surface.ray.direction ).rgb;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if FOG
|
||||
fog( surface.ray, surface.fragment.rgb, surface.fragment.a );
|
||||
#endif
|
||||
@ -331,11 +333,12 @@ void indirectLighting() {
|
||||
|
||||
#if MULTISAMPLING
|
||||
void resolveSurfaceFragment() {
|
||||
msaa.fragment = vec4(0.0);
|
||||
|
||||
for ( int i = 0; i < ubo.settings.mode.msaa; ++i ) {
|
||||
msaa.currentID = i;
|
||||
msaa.IDs[i] = uvec3(IMAGE_LOAD(samplerId)).xy;
|
||||
|
||||
// check if ID is already used
|
||||
bool unique = true;
|
||||
for ( int j = msaa.currentID - 1; j >= 0; --j ) {
|
||||
if ( msaa.IDs[j] == msaa.IDs[i] ) {
|
||||
@ -347,16 +350,24 @@ void resolveSurfaceFragment() {
|
||||
|
||||
if ( unique ) {
|
||||
populateSurface();
|
||||
#if VXGI || RT
|
||||
indirectLighting();
|
||||
#endif
|
||||
directLighting();
|
||||
|
||||
if ( msaa.IDs[i].x == 0 || msaa.IDs[i].y == 0 ) {
|
||||
if ( 0 <= ubo.settings.lighting.indexSkybox && ubo.settings.lighting.indexSkybox < CUBEMAPS ) {
|
||||
surface.fragment.rgb = texture( samplerCubemaps[ubo.settings.lighting.indexSkybox], surface.ray.direction ).rgb;
|
||||
surface.fragment.a = 1.0;
|
||||
}
|
||||
} else {
|
||||
#if VXGI || RT
|
||||
indirectLighting();
|
||||
#endif
|
||||
directLighting();
|
||||
}
|
||||
}
|
||||
|
||||
msaa.fragment += surface.fragment;
|
||||
msaa.fragments[msaa.currentID] = surface.fragment;
|
||||
}
|
||||
|
||||
surface.fragment = msaa.fragment / ubo.settings.mode.msaa;
|
||||
|
||||
surface.fragment = msaa.fragment / float(ubo.settings.mode.msaa);
|
||||
}
|
||||
#endif
|
||||
@ -110,11 +110,12 @@ namespace uf {
|
||||
template<typename T> T loadChild( const uf::Serializer&, bool = true );
|
||||
template<typename T> T loadChild( const uf::stl::string&, bool = true );
|
||||
|
||||
#if UF_HOOKS_HASH_KEYS
|
||||
uf::hashed_string formatHookName( const uf::stl::string& n );
|
||||
static uf::hashed_string formatHookName( const uf::stl::string& n, size_t uid, bool fetch = false );
|
||||
|
||||
inline size_t resolveHookKey(size_t hash) const { return hash; }
|
||||
inline size_t resolveHookKey(const uf::stl::string& name) { return this->formatHookName(name); }
|
||||
inline size_t resolveHookKey( size_t hash ) const { return hash; }
|
||||
inline size_t resolveHookKey( const uf::stl::string& name ) { return this->formatHookName(name); }
|
||||
|
||||
template<typename T> size_t addHook( const size_t& name, T function );
|
||||
template<typename T> inline size_t addHook( const uf::stl::string& name, T function ) {
|
||||
@ -135,6 +136,27 @@ namespace uf {
|
||||
template<typename K, typename... Args> inline uf::Hooks::return_t callHook( const K& name, Args&&... args ) {
|
||||
return uf::hooks.call( this->resolveHookKey(name), std::forward<Args>(args)... );
|
||||
}
|
||||
#else
|
||||
uf::stl::string formatHookName( const uf::stl::string& n );
|
||||
static uf::stl::string formatHookName( const uf::stl::string& n, size_t uid, bool fetch = false );
|
||||
|
||||
template<typename T> size_t addHook( const uf::stl::string& name, T function );
|
||||
|
||||
template<typename K> inline void queueHook( const K& name, float timeout = 0 );
|
||||
template<typename K, typename V> inline void queueHook( const K& name, const V&, float = 0 );
|
||||
|
||||
template<typename K, typename... Args> uf::Hooks::return_t lazyCallHook(const K& name, Args&&... args) {
|
||||
if ( uf::Object::deferLazyCalls ) {
|
||||
this->queueHook(name, std::forward<Args>(args)..., 0.0f);
|
||||
return {};
|
||||
}
|
||||
return this->callHook( name, std::forward<Args>(args)... );
|
||||
}
|
||||
|
||||
template<typename K, typename... Args> inline uf::Hooks::return_t callHook( const K& name, Args&&... args ) {
|
||||
return uf::hooks.call( this->formatHookName( name ), std::forward<Args>(args)... );
|
||||
}
|
||||
#endif
|
||||
|
||||
uf::stl::string resolveURI( const uf::stl::string& filename, const uf::stl::string& root = "" );
|
||||
uf::asset::Payload resolveToPayload( const uf::stl::string& filename, const uf::stl::string& mime = "" );
|
||||
|
||||
@ -17,6 +17,7 @@ T uf::Object::loadChild( const uf::stl::string& filename, bool initialize ) {
|
||||
return this->loadChild(filename, initialize);
|
||||
}
|
||||
|
||||
#if UF_HOOKS_HASH_KEYS
|
||||
template<typename T>
|
||||
size_t uf::Object::addHook( const size_t& name, T callback ) {
|
||||
size_t id = uf::hooks.addHook( name, callback );
|
||||
@ -24,13 +25,23 @@ size_t uf::Object::addHook( const size_t& name, T callback ) {
|
||||
metadata.hooks.bound[name].emplace_back(id);
|
||||
return id;
|
||||
}
|
||||
#else
|
||||
template<typename T>
|
||||
size_t uf::Object::addHook( const uf::stl::string& n, T callback ) {
|
||||
auto name = this->formatHookName( n );
|
||||
size_t id = uf::hooks.addHook( name, callback );
|
||||
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
|
||||
metadata.hooks.bound[name].emplace_back(id);
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename K> inline void uf::Object::queueHook( const K& name, float d ) {
|
||||
auto& metadata = this->getComponent<uf::ObjectBehavior::Metadata>();
|
||||
auto& queue = metadata.hooks.queue.emplace_back(uf::ObjectBehavior::Metadata::Queued{
|
||||
.timeout = uf::time::current + d,
|
||||
});
|
||||
if constexpr ( std::is_same<K, size_t>::value ) {
|
||||
if constexpr ( std::is_same_v<std::decay_t<K>, size_t> ) {
|
||||
queue.hash = name;
|
||||
} else {
|
||||
queue.name = name;
|
||||
@ -43,12 +54,12 @@ void uf::Object::queueHook( const K& name, const V& p, float d ) {
|
||||
auto& queue = metadata.hooks.queue.emplace_back(uf::ObjectBehavior::Metadata::Queued{
|
||||
.timeout = uf::time::current + d,
|
||||
});
|
||||
if constexpr ( std::is_same<K, size_t>::value ) {
|
||||
if constexpr ( std::is_same_v<std::decay_t<K>, size_t> ) {
|
||||
queue.hash = name;
|
||||
} else {
|
||||
queue.name = name;
|
||||
}
|
||||
if constexpr ( std::is_same<V, ext::json::Value>::value ) {
|
||||
if constexpr ( std::is_same_v<std::decay_t<V>, ext::json::Value> ) {
|
||||
queue.type = -1;
|
||||
queue.json = p;
|
||||
} else {
|
||||
|
||||
@ -36,7 +36,7 @@ namespace pod {
|
||||
};
|
||||
}
|
||||
|
||||
#define UF_HOOKS_HASH_KEYS 1
|
||||
#define UF_HOOKS_HASH_KEYS 0
|
||||
|
||||
namespace uf {
|
||||
class UF_API Hooks {
|
||||
|
||||
@ -24,8 +24,8 @@ namespace uf {
|
||||
pod::Atlas::hash_t UF_API add( pod::Atlas& atlas, const pod::Image& image, const pod::Atlas::hash_t& hash );
|
||||
pod::Atlas::hash_t UF_API add( pod::Atlas& atlas, const pod::Image& image );
|
||||
|
||||
void UF_API generate( pod::Atlas& atlas, float padding = 1 );
|
||||
void UF_API generate( pod::Atlas& atlas, const uf::stl::vector<pod::Image>& images, float padding = 1 );
|
||||
void UF_API generate( pod::Atlas& atlas, size_t padding = 0 );
|
||||
void UF_API generate( pod::Atlas& atlas, const uf::stl::vector<pod::Image>& images, size_t padding = 0 );
|
||||
void UF_API clear( pod::Atlas& atlas, bool full = true );
|
||||
bool UF_API has( const pod::Atlas& atlas, const pod::Atlas::hash_t& hash );
|
||||
|
||||
|
||||
@ -22,13 +22,12 @@ namespace uf {
|
||||
typedef size_t hash_t;
|
||||
|
||||
size_t hash;
|
||||
uf::stl::string string;
|
||||
|
||||
constexpr hashed_string() : hash(0) {}
|
||||
constexpr hashed_string(size_t h) : hash(h) {}
|
||||
constexpr hashed_string(const char* s) : hash(uf::algo::fnv1a(s)) {}
|
||||
|
||||
inline hashed_string(const uf::stl::string_view s) : hash(uf::algo::fnv1a(s)) {}
|
||||
inline hashed_string(const uf::stl::string& s) : hash(uf::algo::fnv1a(s)) {}
|
||||
constexpr hashed_string() : hash(0), string("NULL") {}
|
||||
constexpr hashed_string(size_t h) : hash(h), string("NULL") {}
|
||||
constexpr hashed_string(const char* s) : hash(uf::algo::fnv1a(s)), string(s) {}
|
||||
inline hashed_string(const uf::stl::string& s) : hash(uf::algo::fnv1a(s)), string(s) {}
|
||||
|
||||
constexpr operator size_t() const { return hash; }
|
||||
|
||||
|
||||
@ -210,7 +210,7 @@ namespace {
|
||||
|
||||
ext::json::reserve( json["buffers"], mesh.buffers.size() );
|
||||
for ( auto i = 0; i < mesh.buffers.size(); ++i ) {
|
||||
const uf::stl::string filename = ::fmt::format("{}.buffer.{}.{}", settings.filename, i, settings.compression == "none" ? "bin" : settings.compression );
|
||||
const uf::stl::string filename = ::fmt::format("{}.buffer.{}.{}", settings.filename, i, ( settings.compression == "none" ? "bin" : settings.compression ) );
|
||||
uf::io::write( filename, mesh.buffers[i] );
|
||||
json["buffers"].emplace_back(uf::io::filename( filename ));
|
||||
}
|
||||
@ -335,15 +335,15 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string&
|
||||
for ( size_t i = 0; i < graph.images.size(); ++i ) {
|
||||
auto& name = graph.images[i];
|
||||
auto& image = /*graph.storage*/storage.images.map.at(name).data;
|
||||
uf::stl::string f = ::fmt::format("{}/image.{}.png", directory, i );
|
||||
image.save(directory + "/" + f);
|
||||
uf::stl::string f = ::fmt::format("image.{}.png", i );
|
||||
image.save(::fmt::format("{}/{}", directory, f));
|
||||
|
||||
// export DC's .dtex
|
||||
#if UF_USE_DC_TEXCONV
|
||||
// to-do: properly scale per my script
|
||||
auto converted = image.scale( {32, 32}, "nearest" );
|
||||
auto dtex = ext::texconv::convert( converted );
|
||||
ext::texconv::save( dtex, ::fmt::format("{}/image.{}", directory, i ) );
|
||||
ext::texconv::save( dtex, ::fmt::format("{}/image.{}", directory, i) );
|
||||
#endif
|
||||
|
||||
uf::Serializer json;
|
||||
@ -407,9 +407,9 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string&
|
||||
if ( !settings.combined ) {
|
||||
for ( auto i = 0; i < graph.animations.size(); ++i ) {
|
||||
auto& name = graph.animations[i];
|
||||
uf::stl::string f =::fmt::format("animation.{}.json", i);
|
||||
uf::stl::string f = ::fmt::format( "animation.{}.json", i );
|
||||
auto& animation = /*graph.storage*/storage.animations.map.at(name);
|
||||
encode(animation, settings, graph).writeToFile(::fmt::format("{}/{}", directory, f));
|
||||
encode(animation, settings, graph).writeToFile(directory+"/"+f);
|
||||
serializer["animations"].emplace_back(f);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -1229,6 +1229,27 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
node.metadata["door"] = metadataValve["door"];
|
||||
loadJson["imports"].emplace_back("ent://door.json");
|
||||
}
|
||||
// bind prop
|
||||
else if ( ( node.name.starts_with("prop_") || node.name == "func_physbox" ) && ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) ) {
|
||||
auto& meshName = graph.meshes[node.mesh];
|
||||
|
||||
// get flags
|
||||
int spawnflags = metadataValve["spawnflags"].as<int>(0);
|
||||
bool motionDisabled = (spawnflags & 8) != 0;
|
||||
bool preventPickup = (spawnflags & 512) != 0;
|
||||
|
||||
// get mass
|
||||
float baseMass = graph.metadata["valve"]["models"][meshName]["mass"].as<float>(1.0f);
|
||||
float massScale = metadataValve["massScale"].as<float>(1.0f);
|
||||
// flag as static
|
||||
if ( node.name.starts_with("prop_static") || motionDisabled ) massScale = 0;
|
||||
float mass = baseMass * massScale;
|
||||
|
||||
node.metadata["physics"]["type"] = "mesh";
|
||||
node.metadata["physics"]["mass"] = mass;
|
||||
|
||||
node.metadata["holdable"] = (mass <= 35.0f) && !motionDisabled && !preventPickup;
|
||||
}
|
||||
// assume all other funcs are to have a physics body
|
||||
else if ( node.name.starts_with("func_") ) {
|
||||
if ( ext::json::isNull( node.metadata["physics"] ) ) {
|
||||
@ -1237,7 +1258,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
|
||||
node.metadata["physics"]["category"] = "trigger";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check if trigger
|
||||
if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) {
|
||||
auto& primitives = storage.primitives.map[graph.primitives[node.mesh]];
|
||||
|
||||
@ -329,16 +329,20 @@ void uf::ObjectBehavior::tick( uf::Object& self ) {
|
||||
}
|
||||
for ( auto& q : executeQueue ) {
|
||||
if ( q.hash ) {
|
||||
#if UF_UF_HOOKS_HASH_KEYS
|
||||
if ( q.type == 1 ) {
|
||||
this->callHook( q.hash, q.userdata );
|
||||
this->callHook( q.hash, static_cast<const pod::Hook::userdata_t&>(q.userdata) );
|
||||
}
|
||||
else if ( q.type == -1 ) this->callHook( q.hash, q.json );
|
||||
else if ( q.type == -1 ) this->callHook( q.hash, static_cast<const ext::json::Value&>(q.json) );
|
||||
else this->callHook( q.hash );
|
||||
#else
|
||||
UF_EXCEPTION("unimplemented");
|
||||
#endif
|
||||
} else {
|
||||
if ( q.type == 1 ) {
|
||||
this->callHook( q.name, q.userdata );
|
||||
this->callHook( q.name, static_cast<const pod::Hook::userdata_t&>(q.userdata) );
|
||||
}
|
||||
else if ( q.type == -1 ) this->callHook( q.name, q.json );
|
||||
else if ( q.type == -1 ) this->callHook( q.name, static_cast<const ext::json::Value&>(q.json) );
|
||||
else this->callHook( q.name );
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,7 @@ void uf::Object::queueDeletion() {
|
||||
this->callHook("entity:Destroy.%UID%");
|
||||
}
|
||||
|
||||
#if UF_HOOKS_HASH_KEYS
|
||||
uf::hashed_string uf::Object::formatHookName( const uf::stl::string& n, size_t uid, bool fetch ) {
|
||||
if ( fetch ) {
|
||||
auto* object = (uf::Object*) uf::Entity::globalFindByUid( uid );
|
||||
@ -75,7 +76,9 @@ uf::hashed_string uf::Object::formatHookName( const uf::stl::string& n, size_t u
|
||||
uf::hash( hash, n );
|
||||
if ( n.ends_with("%UID%") ) uf::hash( hash, uid );
|
||||
|
||||
return hash;
|
||||
uf::hashed_string res = hash;
|
||||
res.string = n;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@ -88,8 +91,35 @@ uf::hashed_string uf::Object::formatHookName( const uf::stl::string& n ) {
|
||||
if ( n.ends_with("%P-UID%") ) uf::hash( hash, parent );
|
||||
else if ( n.ends_with("%UID%") ) uf::hash( hash, uid );
|
||||
|
||||
return hash;
|
||||
uf::hashed_string res = hash;
|
||||
res.string = n;
|
||||
return res;
|
||||
}
|
||||
#else
|
||||
uf::stl::string uf::Object::formatHookName( const uf::stl::string& n, size_t uid, bool fetch ) {
|
||||
if ( fetch ) {
|
||||
auto* object = (uf::Object*) uf::Entity::globalFindByUid( uid );
|
||||
if ( object ) return object->formatHookName( n );
|
||||
}
|
||||
|
||||
uf::stl::string res = n;
|
||||
if ( n.ends_with("%UID%") ) res = uf::string::replace( res, "%UID%", ::fmt::format( "{}", uid ) );
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
uf::stl::string uf::Object::formatHookName( const uf::stl::string& n ) {
|
||||
size_t uid = this->getUid();
|
||||
size_t parent = this->hasParent() ? this->getParent().getUid() : uid;
|
||||
|
||||
uf::stl::string res = n;
|
||||
if ( n.ends_with("%P-UID%") ) res = uf::string::replace( res, "%P-UID%", ::fmt::format( "{}", parent ) );
|
||||
else if ( n.ends_with("%UID%") ) res = uf::string::replace( res, "%UID%", ::fmt::format( "{}", uid ) );
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool uf::Object::load( const uf::stl::string& f, bool inheritRoot ) {
|
||||
uf::Serializer json;
|
||||
|
||||
@ -12,112 +12,10 @@
|
||||
uf::stl::unordered_map<uf::stl::string, ext::lua::GetComponent> ext::lua::componentGetters;
|
||||
|
||||
namespace binds {
|
||||
/*
|
||||
namespace enums {
|
||||
enum Components {
|
||||
Metadata,
|
||||
Transform,
|
||||
Audio,
|
||||
// Asset,
|
||||
Camera,
|
||||
Physics,
|
||||
PhysicsState,
|
||||
};
|
||||
|
||||
static uf::StaticInitialization TOKEN_PASTE(STATIC_INITIALIZATION_, __LINE__)( []{
|
||||
#define UF_LUA_REGISTER_ENUM(E) #E, enums::Components::E
|
||||
ext::lua::onInitialization( []{
|
||||
auto enums = ext::lua::state.new_enum("Components",
|
||||
UF_LUA_REGISTER_ENUM(Metadata),
|
||||
UF_LUA_REGISTER_ENUM(Transform),
|
||||
UF_LUA_REGISTER_ENUM(Audio),
|
||||
// UF_LUA_REGISTER_ENUM(Asset),
|
||||
UF_LUA_REGISTER_ENUM(Camera),
|
||||
UF_LUA_REGISTER_ENUM(Physics),
|
||||
UF_LUA_REGISTER_ENUM(PhysicsState)
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
uf::hashed_string formatHookName(uf::Object& self, const uf::stl::string n ){
|
||||
return self.formatHookName(n);
|
||||
}
|
||||
|
||||
/*
|
||||
sol::object getComponentFromEnum( uf::Object& self, binds::enums::Components type ) {
|
||||
#define UF_LUA_RETRIEVE_COMPONENT_FROM_ENUM( E, T )\
|
||||
case enums::Components::E: return sol::make_object( ext::lua::state, std::ref(self.getComponent<T>()) );
|
||||
|
||||
switch ( type ) {
|
||||
case enums::Components::Metadata: {
|
||||
self.callHook( "object:Serialize.%UID%" );
|
||||
auto& metadata = self.getComponent<uf::Serializer>();
|
||||
auto decoded = ext::lua::decode( metadata );
|
||||
if ( decoded ) {
|
||||
sol::table table = decoded.value();
|
||||
return sol::make_object( ext::lua::state, table );
|
||||
}
|
||||
UF_MSG_ERROR("Failed to deserialize metadata for {}: {}", self.getName(), self.getUid());
|
||||
} break;
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_ENUM( Transform, pod::Transform<> );
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_ENUM( Audio, uf::Audio );
|
||||
// UF_LUA_RETRIEVE_COMPONENT_FROM_ENUM( Asset, uf::asset );
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_ENUM( Camera, uf::Camera );
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_ENUM( Physics, pod::Physics );
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_ENUM( PhysicsState, pod::PhysicsState );
|
||||
}
|
||||
UF_MSG_ERROR("Invalid component of {} requested for {}: {}", type, self.getName(), self.getUid());
|
||||
|
||||
return sol::make_object( ext::lua::state, sol::lua_nil );
|
||||
}
|
||||
sol::object getComponentFromString( uf::Object& self, const uf::stl::string& type ) {
|
||||
#define UF_LUA_RETRIEVE_COMPONENT_FROM_STRING( T )\
|
||||
else if ( type == UF_NS_GET_LAST(T) ) return sol::make_object( ext::lua::state, std::ref(self.getComponent<T>()) );
|
||||
|
||||
if ( type == "Metadata" ) {
|
||||
self.callHook( "object:Serialize.%UID%" );
|
||||
auto& metadata = self.getComponent<uf::Serializer>();
|
||||
auto decoded = ext::lua::decode( metadata );
|
||||
if ( decoded ) {
|
||||
sol::table table = decoded.value();
|
||||
return sol::make_object( ext::lua::state, table );
|
||||
}
|
||||
UF_MSG_ERROR("Failed to deserialize metadata for {}: {}", self.getName(), self.getUid());
|
||||
}
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_STRING(pod::Transform<>)
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_STRING(uf::Audio)
|
||||
// UF_LUA_RETRIEVE_COMPONENT_FROM_STRING(uf::asset)
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_STRING(uf::Camera)
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_STRING(pod::Physics)
|
||||
UF_LUA_RETRIEVE_COMPONENT_FROM_STRING(pod::PhysicsState)
|
||||
UF_MSG_ERROR("Invalid component of {} requested for {}: {}", type, self.getName(), self.getUid());
|
||||
return sol::make_object( ext::lua::state, sol::lua_nil );
|
||||
}
|
||||
void setComponent(uf::Object& self, const uf::stl::string& type, sol::object value ) {
|
||||
#define UF_LUA_UPDATE_COMPONENT( T )\
|
||||
else if ( type == UF_NS_GET_LAST(T) ) self.getComponent<T>() = std::move(value.as<T>());
|
||||
|
||||
if ( type == "Metadata" ) {
|
||||
auto encoded = ext::lua::encode( value.as<sol::table>() );
|
||||
if ( encoded ) {
|
||||
uf::stl::string str = encoded.value();
|
||||
ext::json::Value json;
|
||||
ext::json::decode( json, str );
|
||||
self.callHook( "object:Deserialize.%UID%", json );
|
||||
}
|
||||
}
|
||||
UF_LUA_UPDATE_COMPONENT(pod::Transform<>)
|
||||
UF_LUA_UPDATE_COMPONENT(uf::Audio)
|
||||
// UF_LUA_UPDATE_COMPONENT(uf::asset)
|
||||
UF_LUA_UPDATE_COMPONENT(uf::Camera)
|
||||
UF_LUA_UPDATE_COMPONENT(pod::Physics)
|
||||
UF_LUA_UPDATE_COMPONENT(pod::PhysicsState)
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
sol::object getComponent( uf::Object& self, const uf::stl::string& type ) {
|
||||
if ( type == "Metadata" ) {
|
||||
self.callHook( "object:Serialize.%UID%" );
|
||||
|
||||
@ -509,6 +509,17 @@ namespace impl {
|
||||
auto meshID = graph.meshes.size();
|
||||
if ( ext::valve::loadMdl(graph, model) ) {
|
||||
node.mesh = meshID;
|
||||
} else {
|
||||
uf::stl::string model = "models/error.mdl";
|
||||
auto it = std::find(graph.meshes.begin(), graph.meshes.end(), model);
|
||||
|
||||
if ( it != graph.meshes.end() ) {
|
||||
node.mesh = (int32_t)std::distance(graph.meshes.begin(), it);
|
||||
} else if ( ext::valve::loadMdl( graph, model ) ) {
|
||||
node.mesh = (int32_t)(graph.meshes.size() - 1);
|
||||
} else {
|
||||
node.mesh = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.mesh = (int32_t)std::distance(graph.meshes.begin(), it);
|
||||
@ -689,7 +700,7 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
|
||||
|
||||
{
|
||||
UF_MSG_DEBUG("Generating new lightmap atlas...");
|
||||
uf::atlas::generate( context.lightmapAtlas, 0.0f );
|
||||
uf::atlas::generate( context.lightmapAtlas, 1 );
|
||||
//UF_MSG_DEBUG("Generated lightmap atlas.");
|
||||
|
||||
auto& imageKey = graph.images.emplace_back("lightmap_atlas");
|
||||
|
||||
@ -106,6 +106,31 @@ namespace impl {
|
||||
int32_t skinindex;
|
||||
int32_t numbodyparts;
|
||||
int32_t bodypartindex;
|
||||
int32_t numlocalattachments;
|
||||
int32_t localattachmentindex;
|
||||
int32_t numlocalnodes;
|
||||
int32_t localnodeindex;
|
||||
int32_t localnodenameindex;
|
||||
int32_t numflexdesc;
|
||||
int32_t flexdescindex;
|
||||
int32_t numflexcontrollers;
|
||||
int32_t flexcontrollerindex;
|
||||
int32_t numflexrules;
|
||||
int32_t flexruleindex;
|
||||
int32_t numikchains;
|
||||
int32_t ikchainindex;
|
||||
int32_t nummouths;
|
||||
int32_t mouthindex;
|
||||
int32_t numlocalposeparameters;
|
||||
int32_t localposeparamindex;
|
||||
int32_t surfacepropindex;
|
||||
int32_t keyvalueindex;
|
||||
int32_t keyvaluesize;
|
||||
int32_t numlocalikautoplaylocks;
|
||||
int32_t localikautoplaylockindex;
|
||||
|
||||
float mass;
|
||||
int32_t contents;
|
||||
|
||||
// etc
|
||||
};
|
||||
@ -192,6 +217,9 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read metadata
|
||||
graph.metadata["valve"]["models"][filename]["mass"] = mdlHdr->mass;
|
||||
|
||||
// Read VVD file
|
||||
uf::stl::string vvdPath = filename.substr(0, filename.find_last_of('.')) + ".vvd";
|
||||
uf::stl::vector<uint8_t> vvdBuffer;
|
||||
|
||||
@ -17,7 +17,7 @@ pod::Atlas::hash_t uf::atlas::add( pod::Atlas& atlas, const pod::Image& image )
|
||||
return uf::atlas::add( atlas, image, uf::image::hash( image ) );
|
||||
}
|
||||
|
||||
void uf::atlas::generate( pod::Atlas& atlas, float padding ) {
|
||||
void uf::atlas::generate( pod::Atlas& atlas, size_t padding ) {
|
||||
if ( atlas.tiles.empty() ) return;
|
||||
|
||||
uf::stl::vector<stbrp_rect> rects;
|
||||
@ -34,15 +34,15 @@ void uf::atlas::generate( pod::Atlas& atlas, float padding ) {
|
||||
|
||||
stbrp_rect rect;
|
||||
rect.id = static_cast<int>(rects.size());
|
||||
rect.w = dim.x;
|
||||
rect.h = dim.y;
|
||||
rect.w = dim.x + padding * 2;
|
||||
rect.h = dim.y + padding * 2;
|
||||
rects.push_back(rect);
|
||||
hashes.push_back(hash);
|
||||
|
||||
area += dim.x * dim.y;
|
||||
}
|
||||
|
||||
size_t side = std::sqrt( area ) * std::max(1.0f, padding);
|
||||
size_t side = std::sqrt( area );
|
||||
pod::Vector2ui size = { std::bit_ceil(side), std::bit_ceil(side) };
|
||||
|
||||
bool all_packed = false;
|
||||
@ -71,8 +71,8 @@ void uf::atlas::generate( pod::Atlas& atlas, float padding ) {
|
||||
auto hash = hashes[rect.id];
|
||||
auto& tile = atlas.tiles[hash];
|
||||
|
||||
tile.coord = { rect.x, rect.y };
|
||||
tile.size = { rect.w, rect.h };
|
||||
tile.coord = { rect.x + padding , rect.y + padding };
|
||||
tile.size = { rect.w - padding * 2, rect.h - padding * 2 };
|
||||
|
||||
auto& image = tile.image;
|
||||
auto& srcBuffer = image.pixels;
|
||||
@ -94,10 +94,44 @@ void uf::atlas::generate( pod::Atlas& atlas, float padding ) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( padding > 0 ) {
|
||||
// top and bottom
|
||||
for ( size_t py = 1; py <= padding; ++py ) {
|
||||
size_t topDstY = tile.coord.y - py;
|
||||
size_t topSrcY = tile.coord.y;
|
||||
size_t botDstY = tile.coord.y + tile.size.y - 1 + py;
|
||||
size_t botSrcY = tile.coord.y + tile.size.y - 1;
|
||||
|
||||
size_t rowBytes = tile.size.x * channels * sizeof(decltype(dstBuffer[0]));
|
||||
memcpy( &dstBuffer[topDstY * size.x * channels + tile.coord.x * channels], &dstBuffer[topSrcY * size.x * channels + tile.coord.x * channels], rowBytes );
|
||||
memcpy( &dstBuffer[botDstY * size.x * channels + tile.coord.x * channels], &dstBuffer[botSrcY * size.x * channels + tile.coord.x * channels], rowBytes );
|
||||
}
|
||||
|
||||
// left and right
|
||||
size_t yStart = tile.coord.y - padding;
|
||||
size_t yCount = tile.size.y + padding * 2;
|
||||
for ( size_t y = 0; y < yCount; ++y ) {
|
||||
size_t currentY = yStart + y;
|
||||
size_t rowStart = currentY * size.x * channels;
|
||||
|
||||
for ( size_t px = 1; px <= padding; ++px ) {
|
||||
size_t leftDstX = tile.coord.x - px;
|
||||
size_t leftSrcX = tile.coord.x;
|
||||
size_t rightDstX = tile.coord.x + tile.size.x - 1 + px;
|
||||
size_t rightSrcX = tile.coord.x + tile.size.x - 1;
|
||||
|
||||
for ( size_t c = 0; c < channels; ++c ) {
|
||||
dstBuffer[rowStart + leftDstX * channels + c] = dstBuffer[rowStart + leftSrcX * channels + c];
|
||||
dstBuffer[rowStart + rightDstX * channels + c] = dstBuffer[rowStart + rightSrcX * channels + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void uf::atlas::generate( pod::Atlas& atlas, const uf::stl::vector<pod::Image>& images, float padding ) {
|
||||
void uf::atlas::generate( pod::Atlas& atlas, const uf::stl::vector<pod::Image>& images, size_t padding ) {
|
||||
for ( auto& image : images ) uf::atlas::add( atlas, image );
|
||||
uf::atlas::generate( atlas, padding );
|
||||
}
|
||||
@ -118,11 +152,12 @@ pod::Vector2f uf::atlas::mapUv( const pod::Atlas& atlas, const pod::Vector2f& uv
|
||||
if ( it != atlas.tiles.end() ) {
|
||||
auto& tile = it->second;
|
||||
auto& size = atlas.image.size;
|
||||
pod::Vector2ui coord = {
|
||||
uv.x * tile.size.x + tile.coord.x,
|
||||
uv.y * tile.size.y + tile.coord.y
|
||||
|
||||
pod::Vector2f coord = {
|
||||
uv.x * (float) tile.size.x + (float) tile.coord.x,
|
||||
uv.y * (float) tile.size.y + (float) tile.coord.y
|
||||
};
|
||||
return pod::Vector2f{ (float) coord.x / (float) size.x, (float) coord.y / (float) size.y };
|
||||
return pod::Vector2f{ coord.x / (float)size.x, coord.y / (float)size.y };
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user