more fixes (chasing down demons because of forgetting to support BGR888......), theoretical HDR image saving/loading and animated textures (to-do: actually animate them)

This commit is contained in:
ecker 2026-06-20 23:10:53 -05:00
parent def459e72c
commit e0fbed4a14
21 changed files with 577 additions and 423 deletions

View File

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

View File

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

View File

@ -402,8 +402,6 @@ uvec4 uvec2_16x4( uvec2 i ) {
void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
Triangle triangle;
Vertex points[3];
float uvHandedness = 1.0;
if ( isValidAddress(addresses.position) ) {
VPos buf = VPos(nonuniformEXT(addresses.position));
#pragma unroll 3
@ -419,31 +417,38 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) points[_].st = buf.v[indices[_]];
}
vec3 e0 = points[1].position - points[0].position;
vec3 e1 = points[2].position - points[0].position;
vec2 dUv1 = points[1].uv - points[0].uv;
vec2 dUv2 = points[2].uv - points[0].uv;
float det = (dUv1.x * dUv2.y - dUv1.y * dUv2.x);
float handedness = (det < 0.0) ? -1.0 : 1.0;
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] );
} else {
vec3 normal = normalize(cross(e0, e1));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) points[_].normal = normal;
}
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; ++_ ) points[_].tangent = vec4( buf.v[indices[_]*4+0], buf.v[indices[_]*4+1], buf.v[indices[_]*4+2], buf.v[indices[_]*4+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;
float det = (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
float r = 1.0f / det;
vec3 tangent_tri = (edge1 * deltaUV2.y - edge2 * deltaUV1.y) * r;
uvHandedness = (det < 0.0) ? -1.0 : 1.0;
vec3 tangent = (e0 * dUv2.y - e1 * dUv1.y);
if ( abs(det) < 0.000001 ) {
tangent = abs(points[0].normal.y) < 0.999 ? vec3(0, 1, 0) : vec3(1, 0, 0);
} else {
tangent /= det;
}
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = tangent_tri;
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = vec4(tangent, handedness);
}
#if BARYCENTRIC_CALCULATE
{
const vec3 p = vec3(inverse( surface.object.model ) * vec4(surface.position.world, 1));
@ -470,12 +475,12 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
}
#endif
// triangle.geomNormal = normalize(cross(points[1].position - points[0].position, points[2].position - points[0].position));
triangle.point.position = /*triangle.*/points[0].position * surface.barycentric[0] + /*triangle.*/points[1].position * surface.barycentric[1] + /*triangle.*/points[2].position * surface.barycentric[2];
triangle.point.normal = /*triangle.*/points[0].normal * surface.barycentric[0] + /*triangle.*/points[1].normal * surface.barycentric[1] + /*triangle.*/points[2].normal * surface.barycentric[2];
triangle.point.uv = /*triangle.*/points[0].uv * surface.barycentric[0] + /*triangle.*/points[1].uv * surface.barycentric[1] + /*triangle.*/points[2].uv * surface.barycentric[2];
triangle.point.st = /*triangle.*/points[0].st * surface.barycentric[0] + /*triangle.*/points[1].st * surface.barycentric[1] + /*triangle.*/points[2].st * surface.barycentric[2];
triangle.point.tangent = /*triangle.*/points[0].tangent * surface.barycentric[0] + /*triangle.*/points[1].tangent * surface.barycentric[1] + /*triangle.*/points[2].tangent * surface.barycentric[2];
triangle.point.position = points[0].position * surface.barycentric[0] + points[1].position * surface.barycentric[1] + points[2].position * surface.barycentric[2];
triangle.point.normal = points[0].normal * surface.barycentric[0] + points[1].normal * surface.barycentric[1] + points[2].normal * surface.barycentric[2];
triangle.point.uv = points[0].uv * surface.barycentric[0] + points[1].uv * surface.barycentric[1] + points[2].uv * surface.barycentric[2];
triangle.point.st = points[0].st * surface.barycentric[0] + points[1].st * surface.barycentric[1] + points[2].st * surface.barycentric[2];
triangle.point.tangent = points[0].tangent * surface.barycentric[0] + points[1].tangent * surface.barycentric[1] + points[2].tangent * surface.barycentric[2];
// bind position (seems to muck with the skybox + fog)
#if 0 && BARYCENTRIC_CALCULATE
@ -490,11 +495,11 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
// surface.normal.eye = vec3( VIEW_MATRIX * vec4(surface.normal.world, 0.0) );
}
// bind tangent
if ( triangle.point.tangent != vec3(0) ) {
surface.tangent.world = normalize(vec3( surface.object.model * vec4(triangle.point.tangent, 0.0) ));
{
surface.tangent.world = vec3( surface.object.model * vec4(triangle.point.tangent.xyz, 0.0) );
surface.tangent.world = normalize(surface.tangent.world - dot(surface.tangent.world, surface.normal.world) * surface.normal.world);
vec3 bitangent = normalize(cross(surface.normal.world, surface.tangent.world)) * sign(triangle.point.tangent.w);
vec3 bitangent = normalize(vec3( surface.object.model * vec4(cross( triangle.point.normal, triangle.point.tangent ) * uvHandedness, 0.0) ));
surface.tbn = mat3(surface.tangent.world, bitangent, surface.normal.world);
}
// bind UVs
@ -598,13 +603,7 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
#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;
@ -636,7 +635,7 @@ void populateSurface( uint instanceID, uint primitiveID ) {
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/indices[_] += drawCommand.vertexID;
for ( uint _ = 0; _ < 3; ++_ ) indices[_] += drawCommand.vertexID;
populateSurface( addresses, indices );
}

View File

@ -303,7 +303,7 @@ struct Vertex {
uint color;
vec2 st;
vec3 normal;
vec3 tangent;
vec4 tangent;
uvec2 joints;
vec4 weights;
};

View File

@ -179,6 +179,21 @@ void postProcess() {
}
}
/*
{
const Material material = materials[surface.instance.materialID >= materials.length() ? 0 : surface.instance.materialID];
int cubemapIndex = -1;
if ( 0 <= surface.instance.cubemapID ) cubemapIndex = surface.instance.cubemapID;
else if ( 0 <= material.indexCubemap ) cubemapIndex = material.indexCubemap;
if ( 0 <= cubemapIndex ) {
const Texture texture = textures[cubemapIndex];
outFragColor.rgb = textureLod(samplerCubemaps[nonuniformEXT(texture.index)], surface.ray.direction, 0).rgb;
}
}
*/
IMAGE_STORE( imageColor, outFragColor );
//IMAGE_STORE( imageBright, outFragBright );
IMAGE_STORE( imageMotion, vec4(outFragMotion, 0, 0) );

View File

@ -10,7 +10,7 @@ layout (location = 1) in vec2 inUv;
layout (location = 2) in vec4 inColor;
layout (location = 3) in vec2 inSt;
layout (location = 4) in vec3 inNormal;
layout (location = 5) in vec3 inTangent;
layout (location = 5) in vec4 inTangent;
#if SKINNED
layout (location = 6) in uvec4 inJoints;
layout (location = 7) in vec4 inWeights;
@ -104,6 +104,6 @@ void main() {
outSt = inSt;
outColor = inColor * object.color;
outNormal = normalize(vec3(model * vec4(inNormal.xyz, 0.0)));
outTangent = normalize(vec3(model * vec4(inTangent.xyz, 0.0)));
outTangent = normalize(vec3(model * vec4(inTangent)));
outBitangent = normalize(vec3(model * vec4(cross( inNormal.xyz, inTangent.xyz ), 0.0)));
}

View File

@ -7,7 +7,7 @@ namespace uf {
pod::Vector4ub color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f st{};
pod::Vector3f normal{};
pod::Vector3f tangent{};
pod::Vector4f tangent{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Base interpolate( const Base& p1, const Base& p2, float t );
@ -18,7 +18,7 @@ namespace uf {
pod::Vector4ub color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f st{};
pod::Vector3f normal{};
pod::Vector3f tangent{};
pod::Vector4f tangent{};
pod::Vector4us joints{};
pod::Vector4f weights{};
@ -32,7 +32,7 @@ namespace uf {
pod::Vector4ub color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f16 st{};
pod::Vector3f16 normal{};
pod::Vector3f16 tangent{};
pod::Vector4f16 tangent{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Base_16f interpolate( const Base_16f& p1, const Base_16f& p2, float t );
@ -43,7 +43,7 @@ namespace uf {
pod::Vector4ub color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f16 st{};
pod::Vector3f16 normal{};
pod::Vector3f16 tangent{};
pod::Vector4f16 tangent{};
pod::Vector4us joints{};
pod::Vector3f16 weights{};
@ -57,7 +57,7 @@ namespace uf {
pod::Vector4ub color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2us st{};
pod::Vector3us normal{};
pod::Vector3us tangent{};
pod::Vector4us tangent{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Base_u16q interpolate( const Base_u16q& p1, const Base_u16q& p2, float t );
@ -68,7 +68,7 @@ namespace uf {
pod::Vector4ub color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2us st{};
pod::Vector3us normal{};
pod::Vector3us tangent{};
pod::Vector4us tangent{};
pod::Vector4us joints{};
pod::Vector3us weights{};

View File

@ -15,6 +15,7 @@ namespace pod {
size_t bpp = 8 * 4;
size_t channels = 4;
size_t format = 0;
size_t layers = 1;
};
}
@ -34,6 +35,8 @@ namespace uf {
pod::Image::pixel_t UF_API at( pod::Image&, const pod::Vector2ui& at );
uf::stl::string UF_API hash( const pod::Image& );
void UF_API layers( pod::Image&, size_t );
void UF_API flip( pod::Image& );
void UF_API padToPowerOfTwo( pod::Image& );
void UF_API convert( pod::Image&, const uf::stl::string&, const uf::stl::string& = "rgba" );
@ -92,6 +95,8 @@ namespace uf {
uf::stl::string getHash() const;
size_t getFormat() const;
void setLayers( size_t );
Image::pixel_t at( const pod::Vector2ui& at );
// Modifiers

View File

@ -795,41 +795,21 @@ void uf::mesh::tangents( uf::stl::vector<T>& vertices ) {
auto uv20 = uv2 - uv0;
auto det = (uv10.x * uv20.y - uv10.y * uv20.x);
float r = 1.0f / det;
float r = (det != 0.0f) ? (1.0f / det) : 0.0f;
auto t = (p10 * uv20.y - p20 * uv10.y) * r;
auto b = (p20 * uv10.x - p10 * uv20.x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& n = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - n * uf::vector::dot(n, t));
if ( uf::vector::dot( uf::vector::cross(n, tangent), b) < 0.0f ) tangent = -tangent;
auto t_ortho = uf::vector::normalize(t - n * uf::vector::dot(n, t));
float w = (uf::vector::dot( uf::vector::cross(n, t_ortho), b) < 0.0f) ? -1.0f : 1.0f;
tangent = { t_ortho.x, t_ortho.y, t_ortho.z, w };
}
/*
pod::Vector3f position[3] = {
vertices[idx[0]].position, vertices[idx[1]].position, vertices[idx[2]].position
};
pod::Vector2f uv[3] = {
vertices[idx[0]].uv, vertices[idx[1]].uv, vertices[idx[2]].uv
};
pod::Vector3f dPosition[2] = { position[1] - position[0], position[2] - position[0] };
pod::Vector2f dUV[2] = { uv[1] - uv[0], uv[2] - uv[0] };
float det = (dUV[0].x * dUV[1].y - dUV[0].y * dUV[1].x);
if ( det == 0.0f ) continue;
float r = 1.0f / det;
auto t = (dPosition[0] * dUV[1].y - dPosition[1] * dUV[0].y) * r;
auto b = (dPosition[1] * dUV[0].x - dPosition[0] * dUV[1].x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& normal = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
if ( uf::vector::dot(uf::vector::cross(normal, tangent), b) < 0.0f ) tangent = -tangent;
}
*/
}
}
@ -856,36 +836,21 @@ void uf::mesh::tangents( uf::stl::vector<T>& vertices, const uf::stl::vector<U>&
auto uv20 = uv2 - uv0;
auto det = (uv10.x * uv20.y - uv10.y * uv20.x);
float r = 1.0f / det;
float r = (det != 0.0f) ? (1.0f / det) : 0.0f;
auto t = (p10 * uv20.y - p20 * uv10.y) * r;
auto b = (p20 * uv10.x - p10 * uv20.x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& n = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - n * uf::vector::dot(n, t));
if ( uf::vector::dot( uf::vector::cross(n, tangent), b) < 0.0f ) tangent = -tangent;
auto t_ortho = uf::vector::normalize(t - n * uf::vector::dot(n, t));
float w = (uf::vector::dot( uf::vector::cross(n, t_ortho), b) < 0.0f) ? -1.0f : 1.0f;
tangent = { t_ortho.x, t_ortho.y, t_ortho.z, w };
}
/*
pod::Vector3f position[3] = { vertices[idx[0]].position, vertices[idx[1]].position, vertices[idx[2]].position };
pod::Vector2f uv[3] = { vertices[idx[0]].uv, vertices[idx[1]].uv, vertices[idx[2]].uv };
pod::Vector3f dPosition[2] = { position[1] - position[0], position[2] - position[0] };
pod::Vector2f dUV[2] = { uv[1] - uv[0], uv[2] - uv[0] };
float det = (dUV[0].x * dUV[1].y - dUV[0].y * dUV[1].x);
if ( det == 0.0f ) continue;
float r = 1.0f / det;
auto t = (dPosition[0] * dUV[1].y - dPosition[1] * dUV[0].y) * r;
auto b = (dPosition[1] * dUV[0].x - dPosition[0] * dUV[1].x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& normal = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
if ( uf::vector::dot(uf::vector::cross(normal, tangent), b) < 0.0f ) tangent = -tangent;
}
*/
}
}

View File

@ -71,13 +71,13 @@ void uf::asset::processQueue() {
bool async = uf::asset::asyncQueue; // a bit buggy
auto tasks = uf::thread::schedule(async ? uf::thread::asyncThreadName : uf::thread::mainThreadName, !true);
if ( !finishedJobs.empty() ) {
tasks.queue([jobs = std::move(finishedJobs)]() {
for ( auto& job : jobs ) {
uf::hooks.call( job.callback, job.payload );
}
});
}
if ( !finishedJobs.empty() ) {
tasks.queue([jobs = std::move(finishedJobs)]() {
for ( auto& job : jobs ) {
uf::hooks.call( job.callback, job.payload );
}
});
}
for ( auto& job : jobs ) tasks.queue([=]{
auto callback = job.callback;
auto type = job.type;

View File

@ -152,14 +152,14 @@ void uf::graph::animate( pod::Graph& graph, const uf::stl::string& _name, float
uf::stl::string name = key + _name;
if ( storage.animations.map.count( name ) == 0 ) {
::loadAnimation( graph, name );
::loadAnimation( graph, name );
}
if ( storage.animations.map.count( name ) > 0 ) {
auto& animation = storage.animations.map[name];
if ( !animation.samplers.empty() && animation.samplers[0].inputs.empty() ) {
::loadAnimation( graph, name );
}
if ( !animation.samplers.empty() && animation.samplers[0].inputs.empty() ) {
::loadAnimation( graph, name );
}
// if already playing, ignore it
if ( !graph.sequence.empty() && graph.sequence.front() == name ) return;
@ -262,7 +262,7 @@ void uf::graph::updateAnimation( pod::Graph& graph, pod::Node& node ) {
auto& skinName = graph.skins[node.skin];
auto& skin = storage.skins[skinName];
auto objectKeyName = ::keyedID(node.object);
auto& joints = storage.joints[objectKeyName];
auto& joints = storage.joints[objectKeyName];
joints.resize( skin.joints.size() );
for ( size_t i = 0; i < skin.joints.size(); ++i ) {
auto nodeID = skin.joints[i];

View File

@ -29,7 +29,7 @@ namespace {
uf::Image image;
uf::stl::string filename = "";
size_t offset = 0, length = 0;
size_t offset = 0, length = 0, layers = json["layers"].as<size_t>(1);
uf::stl::string formatHint = "";
#if UF_ENV_DREAMCAST
@ -51,6 +51,7 @@ namespace {
size_t channels = json["channels"].as<size_t>();
auto pixels = uf::base64::decode( json["data"].as<uf::stl::string>() );
image.loadFromBuffer( &pixels[0], size, bpp, channels, true );
image.setLayers( layers );
return image;
}
@ -69,6 +70,7 @@ namespace {
}
uf::image::open( image, buffer, formatHint, false );
uf::image::layers( image, layers );
image.setFilename(fullPath);
}
@ -76,10 +78,10 @@ namespace {
}
pod::Animation decodeAnimation( ext::json::Value& json, pod::Graph& graph, const uf::stl::string& animName, const uf::stl::vector<uint8_t>& megaBuffer ) {
pod::Animation animation = {};
animation.name = json["name"].as(animation.name);
animation.start = json["start"].as<float>(0.0f);
animation.end = json["end"].as<float>(1.0f);
pod::Animation animation = {};
animation.name = json["name"].as(animation.name);
animation.start = json["start"].as<float>(0.0f);
animation.end = json["end"].as<float>(1.0f);
uf::stl::string binPath = "";
if (json["buffer"].is<uf::stl::string>()) {

View File

@ -406,6 +406,7 @@ uf::stl::string uf::graph::save( const pod::Graph& graph, const uf::stl::string&
json["offset"] = offset;
json["length"] = length;
}
json["layers"] = image.layers;
#if UF_USE_DC_TEXCONV
auto converted = image.scale( {32, 32}, "nearest" );

View File

@ -531,7 +531,7 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base,
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R8G8B8A8_UNORM, color)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R32G32_SFLOAT, st)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R32G32B32_SFLOAT, normal)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R32G32B32_SFLOAT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base, R32G32B32A32_SFLOAT, tangent)
);
// it'd be super sugoi if I could somehow macro this annoyance
UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base, {
@ -551,7 +551,7 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned,
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R8G8B8A8_UNORM, color)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32_SFLOAT, st)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32_SFLOAT, normal)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32_SFLOAT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32A32_SFLOAT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R16G16B16A16_UINT, joints)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned, R32G32B32A32_SFLOAT, weights)
);
@ -575,7 +575,7 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base_16f,
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R8G8B8A8_UNORM, color)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16_SFLOAT, st)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16B16_SFLOAT, normal)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16B16_SFLOAT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_16f, R16G16B16A16_SFLOAT, tangent)
);
UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base_16f, {
return t < 0.5 ? p1 : p2;
@ -587,7 +587,7 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_16f,
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R8G8B8A8_UNORM, color)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16_SFLOAT, st)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16_SFLOAT, normal)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16_SFLOAT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16A16_SFLOAT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16A16_UINT, joints)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_16f, R16G16B16A16_SFLOAT, weights)
);
@ -602,7 +602,7 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Base_u16q,
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R8G8B8A8_UNORM, color)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16_UINT, st)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16B16_UINT, normal)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16B16_UINT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Base_u16q, R16G16B16A16_UINT, tangent)
);
UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base_u16q, {
return t < 0.5 ? p1 : p2;
@ -614,7 +614,7 @@ UF_VERTEX_DESCRIPTOR(uf::graph::mesh::Skinned_u16q,
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R8G8B8A8_UNORM, color)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16_UINT, st)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16_UINT, normal)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16_UINT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16A16_UINT, tangent)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16A16_UINT, joints)
UF_VERTEX_DESCRIPTION(uf::graph::mesh::Skinned_u16q, R16G16B16A16_UINT, weights)
);
@ -946,6 +946,11 @@ void uf::graph::process( pod::Graph& graph ) {
auto texName = graph.textures[material.indexAlbedo];
textureDescriptors[texName].srgb = true;
}
if ( (0 <= material.indexNormal && material.indexNormal < graph.textures.size() ) ) {
auto texName = graph.textures[material.indexNormal];
textureDescriptors[texName].srgb = false;
}
}
UF_DEBUG_TIMER_MULTITRACE("Processing images...");

View File

@ -147,17 +147,17 @@ bool UF_API ext::texconv::save( const pod::Dtex& dtex, const uf::stl::string& fi
}
bool UF_API ext::texconv::save( const pod::Dtex& dtex, uf::stl::vector<uint8_t>& buffer ) {
size_t totalSize = dtex.imageData.size() + dtex.paletteData.size();
buffer.reserve(buffer.size() + totalSize);
buffer.reserve(buffer.size() + totalSize);
if ( !dtex.imageData.empty() ) {
buffer.insert( buffer.end(), dtex.imageData.begin(), dtex.imageData.end() );
}
if ( !dtex.imageData.empty() ) {
buffer.insert( buffer.end(), dtex.imageData.begin(), dtex.imageData.end() );
}
if ( !dtex.paletteData.empty() ) {
buffer.insert( buffer.end(), dtex.paletteData.begin(), dtex.paletteData.end() );
}
if ( !dtex.paletteData.empty() ) {
buffer.insert( buffer.end(), dtex.paletteData.begin(), dtex.paletteData.end() );
}
return true;
return true;
}
// maintains original main()

View File

@ -436,12 +436,6 @@ namespace impl {
pod::Vector2f finalSt;
finalSt.x = tx;
finalSt.y = ty;
/*
finalSt.x = (uf::vector::dot( vBase, texInfo.lightmapVecs[0] ) + 0.5f - face.lightmapTextureMins.x) / (face.lightmapTextureSize.x + 1.0f);
finalSt.y = (uf::vector::dot( vBase, texInfo.lightmapVecs[1] ) + 0.5f - face.lightmapTextureMins.y) / (face.lightmapTextureSize.y + 1.0f);
//finalSt.x = 1.0f - finalSt.x; // ?
finalSt.y = 1.0f - finalSt.y; // ?
*/
int dispIdx = info.dispVertStart + y * side + x;
const auto& dVert = context.dispverts[dispIdx];
@ -469,10 +463,14 @@ namespace impl {
pod::Vector3f normal = uf::vector::normalize(uf::vector::cross(bitangent, tangent));
// float w = (uf::vector::dot(uf::vector::cross(normal, tangent), bitangent) < 0.0f) ? -1.0f : 1.0f;
pod::Vector3f t = uf::vector::normalize( impl::convertPos( texInfo.textureVecs[0], 1.0f ) );
pod::Vector3f b = uf::vector::normalize( impl::convertPos( texInfo.textureVecs[1], 1.0f ) );
t = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
float w = (uf::vector::dot( uf::vector::cross(normal, t), b ) < 0.0f) ? -1.0f : 1.0f;
meshlet.vertices[id].normal = normal;
meshlet.vertices[id].tangent = tangent = uf::vector::normalize(tangent - normal * uf::vector::dot(normal, tangent));
meshlet.vertices[id].tangent = { t.x, t.y, t.z, w };
}
}
@ -520,6 +518,7 @@ namespace impl {
v.position = pos;
v.color = { 1.0f, 1.0f, 1.0f, 1.0f };
v.normal = normal;
v.tangent = { 1.0f, 0.0f, 0.0f, 1.0f };
// has texture information
if ( face.texinfo >= 0 && face.texinfo < context.texinfos.size() ) {
@ -539,8 +538,10 @@ namespace impl {
pod::Vector3f t = uf::vector::normalize( impl::convertPos( info.textureVecs[0], 1.0f ) );
pod::Vector3f b = uf::vector::normalize( impl::convertPos( info.textureVecs[1], 1.0f ) );
v.tangent = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
// float w = (uf::vector::dot( uf::vector::cross(normal, t), b ) < 0.0f) ? -1.0f : 1.0f;
t = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
float w = (uf::vector::dot( uf::vector::cross(normal, t), b ) < 0.0f) ? -1.0f : 1.0f;
v.tangent = { t.x, t.y, t.z, w };
}
};
@ -1047,10 +1048,14 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
uint16_t pivotVertID = pivotSurfEdge >= 0 ? context.edges[pivotSurfEdge].x : context.edges[-pivotSurfEdge].y;
pod::Vector3f p0 = impl::convertPos( context.vertices[pivotVertID] );
/*
// some faces are wrong when doing it this way
const auto& plane = context.planes[face.planenum];
pod::Vector3f faceNormal = uf::vector::normalize( impl::convertPos( plane.normal, 1.0f ) );
if ( face.side != 0 ) faceNormal = -faceNormal;
*/
pod::Vector3f faceNormal = {0.0f, 0.0f, 0.0f};
for ( int16_t i = 1; i < face.numedges - 1; ++i ) {
int32_t se1 = context.surfedges[edgeID + i];
int32_t se2 = context.surfedges[edgeID + i + 1];
@ -1063,7 +1068,6 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
faceNormal += uf::vector::cross(p1 - p0, p2 - p0);
}
faceNormal = uf::vector::normalize(faceNormal);
for ( int16_t i = 1; i < face.numedges - 1; ++i ) {
@ -1197,9 +1201,6 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
// load materials
uf::stl::vector<uint8_t> missing_pixels = { 255, 0, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 0, 255, 255 };
for ( auto matName : graph.materials ) {
if ( matName == "" ) {
continue;
}
uf::Serializer vmt;
auto vmtPath = ::fmt::format("materials/{}.vmt", matName);
auto vtfPath = ::fmt::format("materials/{}.vtf", matName);
@ -1256,7 +1257,7 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
}
// bumpmap
if ( vmt["$ssbump"].as<int>(0) == 1 ) {
material.indexNormal = -1;
// to-do: handle bumpmaps
// normal map
} else if ( vmt["$bumpmap"].is<uf::stl::string>() ) {
auto matName = uf::string::lowercase(vmt["$bumpmap"].as<uf::stl::string>());
@ -1270,7 +1271,9 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
impl::addMaterial( graph, matName, textureID );
auto& image = storage.images[matName].data;
if ( ext::valve::loadVtf( image, vtfPath ) ) material.indexNormal = textureID;
if ( ext::valve::loadVtf( image, vtfPath ) ) {
material.indexNormal = textureID;
}
}
}
// metallic/roughness/occlusion map

View File

@ -34,6 +34,11 @@ size_t impl::addMaterial( pod::Graph& graph, const uf::stl::string& name, int32_
material.factorMetallic = 0.0f;
material.factorRoughness = 1.0f;
material.factorOcclusion = 1.0f;
material.indexNormal = -1;
material.indexEmissive = -1;
material.indexMetallicRoughness = -1;
material.indexOcclusion = -1;
material.indexCubemap = -1;
return materialID;
}

View File

@ -320,66 +320,69 @@ bool ext::valve::loadMdl( pod::Graph& graph, const uf::stl::string& filename ) {
const impl::mstudiomesh_t* mdlMeshes = (const impl::mstudiomesh_t*)((uint8_t*)&mdlModels[m] + mdlModels[m].meshindex);
for ( int meshID = 0; meshID < lod0.numMeshes; ++meshID ) {
const impl::vtxMesh_t& mesh = meshes[meshID];
const impl::mstudiomesh_t& mdlMesh = mdlMeshes[meshID];
const impl::vtxMesh_t& mesh = meshes[meshID];
const impl::mstudiomesh_t& mdlMesh = mdlMeshes[meshID];
auto& meshlet = meshlets.emplace_back();
uf::stl::unordered_map<uint16_t, uint32_t> vertRemap;
auto& meshlet = meshlets.emplace_back();
uf::stl::unordered_map<uint16_t, uint32_t> vertRemap;
const impl::vtxStripGroup_t* stripGroups = (const impl::vtxStripGroup_t*)((uint8_t*)&mesh + mesh.stripGroupHeaderOffset);
for ( int sg = 0; sg < mesh.numStripGroups; ++sg ) {
const impl::vtxStripGroup_t& stripGroup = stripGroups[sg];
const impl::vtxStripGroup_t* stripGroups = (const impl::vtxStripGroup_t*)((uint8_t*)&mesh + mesh.stripGroupHeaderOffset);
for ( int sg = 0; sg < mesh.numStripGroups; ++sg ) {
const impl::vtxStripGroup_t& stripGroup = stripGroups[sg];
const uint16_t* indices = (const uint16_t*)((uint8_t*)&stripGroup + stripGroup.indexOffset);
const impl::vtxVertex_t* vtxVerts = (const impl::vtxVertex_t*)((uint8_t*)&stripGroup + stripGroup.vertOffset);
const uint16_t* indices = (const uint16_t*)((uint8_t*)&stripGroup + stripGroup.indexOffset);
const impl::vtxVertex_t* vtxVerts = (const impl::vtxVertex_t*)((uint8_t*)&stripGroup + stripGroup.vertOffset);
for ( int i = 0; i < stripGroup.numIndices; i += 3 ) {
uint32_t tri[3];
for ( int j = 0; j < 3; ++j ) {
uint16_t localVertIndex = indices[i + j];
const impl::vtxVertex_t& vtxVert = vtxVerts[localVertIndex];
uint16_t originalVvdID = vtxVert.origMeshVertID;
for ( int i = 0; i < stripGroup.numIndices; i += 3 ) {
uint32_t tri[3];
for ( int j = 0; j < 3; ++j ) {
uint16_t localVertIndex = indices[i + j];
const impl::vtxVertex_t& vtxVert = vtxVerts[localVertIndex];
uint16_t originalVvdID = vtxVert.origMeshVertID;
if ( vertRemap.find(originalVvdID) == vertRemap.end() ) {
vertRemap[originalVvdID] = meshlet.vertices.size();
auto& vert = meshlet.vertices.emplace_back();
if ( vertRemap.find(originalVvdID) == vertRemap.end() ) {
vertRemap[originalVvdID] = meshlet.vertices.size();
auto& vert = meshlet.vertices.emplace_back();
const auto& srcVert = lod0Vertices[mdlMesh.vertexoffset + originalVvdID];
const auto& srcVert = lod0Vertices[mdlMesh.vertexoffset + originalVvdID];
vert.position = impl::convertPos( srcVert.m_vecPosition );
vert.normal = uf::vector::normalize( impl::convertPos( srcVert.m_vecNormal, 1.0f ) );
vert.position = impl::convertPos( srcVert.m_vecPosition );
vert.normal = uf::vector::normalize( impl::convertPos( srcVert.m_vecNormal, 1.0f ) );
vert.tangent = {0.0f, 0.0f, 0.0f, 1.0f};
if ( !lod0Tangents.empty() ) {
vert.tangent = uf::vector::normalize( impl::convertPos( lod0Tangents[mdlMesh.vertexoffset + originalVvdID], 1.0f ) );
}
if ( !lod0Tangents.empty() ) {
auto srcTangent = lod0Tangents[mdlMesh.vertexoffset + originalVvdID];
pod::Vector3f tangent = uf::vector::normalize( impl::convertPos( srcTangent, 1.0f ) );
vert.tangent = { tangent.x, tangent.y, tangent.z, srcTangent.w }; // might need to -w
}
vert.uv = srcVert.m_vecTexCoord;
vert.color = {1.0f, 1.0f, 1.0f, 1.0f};
vert.joints.x = srcVert.m_BoneWeights.numbones > 0 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[0]) : 0;
vert.joints.y = srcVert.m_BoneWeights.numbones > 1 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[1]) : 0;
vert.joints.z = srcVert.m_BoneWeights.numbones > 2 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[2]) : 0;
vert.joints.w = 0;
vert.uv = srcVert.m_vecTexCoord;
vert.color = {1.0f, 1.0f, 1.0f, 1.0f};
vert.joints.x = srcVert.m_BoneWeights.numbones > 0 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[0]) : 0;
vert.joints.y = srcVert.m_BoneWeights.numbones > 1 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[1]) : 0;
vert.joints.z = srcVert.m_BoneWeights.numbones > 2 ? std::max<int8_t>(0, srcVert.m_BoneWeights.bone[2]) : 0;
vert.joints.w = 0;
vert.weights.x = srcVert.m_BoneWeights.numbones > 0 ? srcVert.m_BoneWeights.weight[0] : 1.0f;
vert.weights.y = srcVert.m_BoneWeights.numbones > 1 ? srcVert.m_BoneWeights.weight[1] : 0.0f;
vert.weights.z = srcVert.m_BoneWeights.numbones > 2 ? srcVert.m_BoneWeights.weight[2] : 0.0f;
vert.weights.w = 0.0f;
vert.weights.x = srcVert.m_BoneWeights.numbones > 0 ? srcVert.m_BoneWeights.weight[0] : 1.0f;
vert.weights.y = srcVert.m_BoneWeights.numbones > 1 ? srcVert.m_BoneWeights.weight[1] : 0.0f;
vert.weights.z = srcVert.m_BoneWeights.numbones > 2 ? srcVert.m_BoneWeights.weight[2] : 0.0f;
vert.weights.w = 0.0f;
auto& bounds = meshlet.primitive.instance.bounds;
if ( vertRemap.size() == 1 ) {
bounds.min = bounds.max = vert.position;
} else {
bounds.min = uf::vector::min( bounds.min, vert.position );
bounds.max = uf::vector::max( bounds.max, vert.position );
}
}
auto& bounds = meshlet.primitive.instance.bounds;
if ( vertRemap.size() == 1 ) {
bounds.min = bounds.max = vert.position;
} else {
bounds.min = uf::vector::min( bounds.min, vert.position );
bounds.max = uf::vector::max( bounds.max, vert.position );
}
}
meshlet.indices.push_back(tri[j] = vertRemap[originalVvdID]);
}
}
}
meshlet.indices.emplace_back(tri[j] = vertRemap[originalVvdID]);
}
}
}
if ( lod0Tangents.empty() ) uf::mesh::tangents( meshlet.vertices, meshlet.indices );
if ( lod0Tangents.empty() ) uf::mesh::tangents( meshlet.vertices, meshlet.indices );
size_t materialID = 0;
uf::stl::string matName = "missing_texture";

View File

@ -6,13 +6,80 @@
namespace impl {
constexpr uint32_t IMAGE_FORMAT_RGBA8888 = 0;
constexpr uint32_t IMAGE_FORMAT_RGB888 = 2;
constexpr uint32_t IMAGE_FORMAT_BGR888 = 3;
constexpr uint32_t IMAGE_FORMAT_BGR565 = 4;
constexpr uint32_t IMAGE_FORMAT_BGRX8888 = 11;
constexpr uint32_t IMAGE_FORMAT_BGRA8888 = 12;
constexpr uint32_t IMAGE_FORMAT_DXT1 = 13;
constexpr uint32_t IMAGE_FORMAT_DXT3 = 14;
constexpr uint32_t IMAGE_FORMAT_DXT5 = 15;
constexpr uint32_t IMAGE_FORMAT_RGBA16161616F = 24;
constexpr uint32_t TEXTUREFLAGS_ENVMAP = 0x00002000;
constexpr uint32_t TEXTUREFLAGS_NORMAL = 0x00000080;
constexpr uint32_t MATERIAL_VAR_DEBUG = 0x0001; // $debug
constexpr uint32_t MATERIAL_VAR_NO_DEBUG_OVERRIDE = 0x0002; // $no_fullbright
constexpr uint32_t MATERIAL_VAR_NO_DRAW = 0x0004; // $no_draw
constexpr uint32_t MATERIAL_VAR_USE_IN_FILLRATE_MODE = 0x0008; // $use_in_fillrate_mode
constexpr uint32_t MATERIAL_VAR_VERTEXCOLOR = 0x0010; // $vertexcolor
constexpr uint32_t MATERIAL_VAR_VERTEXALPHA = 0x0020; // $vertexalpha
constexpr uint32_t MATERIAL_VAR_SELFILLUM = 0x0040; // $selfillum
constexpr uint32_t MATERIAL_VAR_ADDITIVE = 0x0080; // $additive
constexpr uint32_t MATERIAL_VAR_ALPHATEST = 0x0100; // $alphatest
constexpr uint32_t MATERIAL_VAR_MULTIPASS = 0x0200; // $multipass
constexpr uint32_t MATERIAL_VAR_ZNEARER = 0x0400; // $znearer
constexpr uint32_t MATERIAL_VAR_MODEL = 0x0800; // $model
constexpr uint32_t MATERIAL_VAR_FLAT = 0x1000; // $flat
constexpr uint32_t MATERIAL_VAR_NOCULL = 0x2000; // $nocull
constexpr uint32_t MATERIAL_VAR_NOFOG = 0x4000; // $nofog
constexpr uint32_t MATERIAL_VAR_IGNOREZ = 0x8000; // $ignorez
constexpr uint32_t MATERIAL_VAR_DECAL = 0x10000; // $decal
constexpr uint32_t MATERIAL_VAR_ENVMAPSPHERE = 0x20000; // $envmapsphere
constexpr uint32_t MATERIAL_VAR_NOALPHAMOD = 0x40000; // $noalphamod
constexpr uint32_t MATERIAL_VAR_ENVMAPCAMERASPACE = 0x80000; // $envmapcameraspace
constexpr uint32_t MATERIAL_VAR_BASEALPHAENVMAPMASK = 0x100000; // $basealphaenvmapmask
constexpr uint32_t MATERIAL_VAR_TRANSLUCENT = 0x200000; // $translucent
constexpr uint32_t MATERIAL_VAR_NORMALMAPALPHAENVMAPMASK = 0x400000; // $normalmapalphaenvmapmask
constexpr uint32_t MATERIAL_VAR_NEEDS_SOFTWARE_SKINNING = 0x800000; // $softwareskin
constexpr uint32_t MATERIAL_VAR_OPAQUETEXTURE = 0x1000000; // $opaquetexture
constexpr uint32_t MATERIAL_VAR_ENVMAPMODE = 0x2000000; // $envmapmode
constexpr uint32_t MATERIAL_VAR_SUPPRESS_DECALS = 0x4000000; // $nodecal
constexpr uint32_t MATERIAL_VAR_HALFLAMBERT = 0x8000000; // $halflambert
constexpr uint32_t MATERIAL_VAR_WIREFRAME = 0x10000000; // $wireframe
constexpr uint32_t MATERIAL_VAR_ALLOWALPHATOCOVERAGE = 0x20000000; // $allowalphatocoverage
constexpr uint32_t MATERIAL_VAR_IGNORE_ALPHA_MODULATION = 0x40000000; //
constexpr uint32_t TEXTUREFLAGS_POINTSAMPLE = 1;
constexpr uint32_t TEXTUREFLAGS_TRILINEAR = 2;
constexpr uint32_t TEXTUREFLAGS_CLAMPS = 4;
constexpr uint32_t TEXTUREFLAGS_CLAMPT = 8;
constexpr uint32_t TEXTUREFLAGS_ANISOTROPIC = 16;
constexpr uint32_t TEXTUREFLAGS_HINT_DXT5 = 32;
constexpr uint32_t TEXTUREFLAGS_PWL_CORRECTED = 64;
constexpr uint32_t TEXTUREFLAGS_NORMAL = 128;
constexpr uint32_t TEXTUREFLAGS_NOMIP = 256;
constexpr uint32_t TEXTUREFLAGS_NOLOD = 512;
constexpr uint32_t TEXTUREFLAGS_ALL_MIPS = 1024;
constexpr uint32_t TEXTUREFLAGS_PROCEDURAL = 2048;
constexpr uint32_t TEXTUREFLAGS_ONEBITALPHA = 4096;
constexpr uint32_t TEXTUREFLAGS_EIGHTBITALPHA = 8192;
constexpr uint32_t TEXTUREFLAGS_ENVMAP = 16384;
constexpr uint32_t TEXTUREFLAGS_RENDERTARGET = 32768;
constexpr uint32_t TEXTUREFLAGS_DEPTHRENDERTARGET = 65536;
constexpr uint32_t TEXTUREFLAGS_NODEBUGOVERRIDE = 131072;
constexpr uint32_t TEXTUREFLAGS_SINGLECOPY = 262144;
constexpr uint32_t TEXTUREFLAGS_STAGING_MEMORY = 524288;
constexpr uint32_t TEXTUREFLAGS_IMMEDIATE_CLEANUP = 1048576;
constexpr uint32_t TEXTUREFLAGS_IGNORE_PICMIP = 2097152;
constexpr uint32_t TEXTUREFLAGS_UNUSED_00400000 = 4194304;
constexpr uint32_t TEXTUREFLAGS_NODEPTHBUFFER = 8388608;
constexpr uint32_t TEXTUREFLAGS_UNUSED_01000000 = 16777216;
constexpr uint32_t TEXTUREFLAGS_CLAMPU = 33554432;
constexpr uint32_t TEXTUREFLAGS_VERTEXTEXTURE = 67108864;
constexpr uint32_t TEXTUREFLAGS_SSBUMP = 134217728;
constexpr uint32_t TEXTUREFLAGS_UNUSED_10000000 = 268435456;
constexpr uint32_t TEXTUREFLAGS_BORDER = 536870912;
constexpr uint32_t TEXTUREFLAGS_STREAMABLE_COARSE = 1073741824;
constexpr uint32_t TEXTUREFLAGS_STREAMABLE_FINE = 2147483648;
#pragma pack(push, 1)
struct VTFHeader {
@ -35,6 +102,32 @@ namespace impl {
};
#pragma pack(pop)
inline size_t getMipSize( uint32_t format, int width, int height ) {
int blocksX = (width + 3) / 4;
int blocksY = (height + 3) / 4;
switch ( format ) {
case impl::IMAGE_FORMAT_DXT1:
return blocksX * blocksY * 8;
case impl::IMAGE_FORMAT_DXT3:
case impl::IMAGE_FORMAT_DXT5:
return blocksX * blocksY * 16;
case impl::IMAGE_FORMAT_BGR888:
case impl::IMAGE_FORMAT_RGB888:
return width * height * 3;
case impl::IMAGE_FORMAT_BGRA8888:
case impl::IMAGE_FORMAT_BGRX8888:
case impl::IMAGE_FORMAT_RGBA8888:
return width * height * 4;
case impl::IMAGE_FORMAT_BGR565:
return width * height * 2;
case impl::IMAGE_FORMAT_RGBA16161616F:
return width * height * 8;
default:
return 0;
}
};
// to-do: cram this inside the image functions
inline void decodeRGB565( uint16_t color, uint8_t& r, uint8_t& g, uint8_t& b ) {
r = (uint8_t)(((color >> 11) & 0x1F) * 255 / 31);
@ -42,6 +135,32 @@ namespace impl {
b = (uint8_t)((color & 0x1F) * 255 / 31);
}
inline uint8_t halfTo8Bit(uint16_t h) {
uint32_t exp = (h >> 10) & 0x1F;
uint32_t mant = h & 0x3FF;
if (exp == 0) return 0;
if (exp == 31) return 255;
exp = exp + (127 - 15);
uint32_t f = (exp << 23) | (mant << 13);
float val;
std::memcpy(&val, &f, sizeof(float));
return (uint8_t)(std::min(std::max(val, 0.0f), 1.0f) * 255.0f);
}
inline float halfToFloat(uint16_t h) {
uint32_t exp = (h >> 10) & 0x1F;
uint32_t mant = h & 0x3FF;
if (exp == 0 && mant == 0) return 0.0f;
exp = exp + (127 - 15);
uint32_t f = (exp << 23) | (mant << 13);
float val;
std::memcpy(&val, &f, sizeof(float));
return val;
}
void decompressDXT1Block( const uint8_t* block, uint8_t* out, int x, int y, int width, int height ) {
uint16_t color0 = *(const uint16_t*)(block + 0);
uint16_t color1 = *(const uint16_t*)(block + 2);
@ -73,6 +192,38 @@ namespace impl {
}
}
void decompressDXT3Block(const uint8_t* block, uint8_t* out, int x, int y, int width, int height) {
uint16_t color0 = *(const uint16_t*)(block + 8);
uint16_t color1 = *(const uint16_t*)(block + 10);
uint32_t colorIndices = *(const uint32_t*)(block + 12);
uint8_t r[4], g[4], b[4];
decodeRGB565(color0, r[0], g[0], b[0]);
decodeRGB565(color1, r[1], g[1], b[1]);
r[2] = (2 * r[0] + r[1]) / 3; g[2] = (2 * g[0] + g[1]) / 3; b[2] = (2 * b[0] + b[1]) / 3;
r[3] = (r[0] + 2 * r[1]) / 3; g[3] = (g[0] + 2 * g[1]) / 3; b[3] = (b[0] + 2 * b[1]) / 3;
for ( int py = 0; py < 4; ++py ) {
for ( int px = 0; px < 4; ++px ) {
if (x + px >= width || y + py >= height) continue;
uint8_t colorIdx = (colorIndices >> ((py * 4 + px) * 2)) & 0x03;
int alphaOffset = (py * 4 + px) / 2;
int alphaShift = ((py * 4 + px) % 2) * 4;
uint8_t alpha4 = (block[alphaOffset] >> alphaShift) & 0x0F;
uint8_t alpha = (alpha4 << 4) | alpha4;
int offset = ((y + py) * width + (x + px)) * 4;
out[offset + 0] = r[colorIdx];
out[offset + 1] = g[colorIdx];
out[offset + 2] = b[colorIdx];
out[offset + 3] = alpha;
}
}
}
void decompressDXT5Block(const uint8_t* block, uint8_t* out, int x, int y, int width, int height) {
uint8_t a0 = block[0];
uint8_t a1 = block[1];
@ -155,158 +306,173 @@ bool ext::valve::loadVtf( pod::Image& image, const uf::stl::string& filename ) {
const impl::VTFHeader* header = (const impl::VTFHeader*)(buffer.data());
if ( strncmp(header->signature, "VTF", 3) != 0 ) return false;
bool isCubemap = (header->flags & impl::TEXTUREFLAGS_ENVMAP) != 0;
int numFrames = std::max<int>(1, header->frames);
switch ( header->highResImageFormat ) {
case impl::IMAGE_FORMAT_RGBA8888:
case impl::IMAGE_FORMAT_RGB888:
case impl::IMAGE_FORMAT_BGR888:
case impl::IMAGE_FORMAT_BGR565:
case impl::IMAGE_FORMAT_BGRX8888:
case impl::IMAGE_FORMAT_BGRA8888:
case impl::IMAGE_FORMAT_DXT1:
case impl::IMAGE_FORMAT_DXT3:
case impl::IMAGE_FORMAT_DXT5:
case impl::IMAGE_FORMAT_RGBA16161616F: {
break;
}
default: {
UF_MSG_ERROR("VTF '{}' has unrecognized format: 0x{:x}", filename, header->highResImageFormat);
} break;
}
size_t singleFaceSize = 0;
for ( int mip = header->mipmapCount - 1; mip >= 0; --mip ) {
for ( int mip = 0; mip < header->mipmapCount; ++mip ) {
int mipWidth = std::max(1, header->width >> mip);
int mipHeight = std::max(1, header->height >> mip);
int blocksX = (mipWidth + 3) / 4;
int blocksY = (mipHeight + 3) / 4;
if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT1 ) singleFaceSize += blocksX * blocksY * 8;
else if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT5 ) singleFaceSize += blocksX * blocksY * 16;
else if ( header->highResImageFormat == impl::IMAGE_FORMAT_BGRA8888 ) singleFaceSize += mipWidth * mipHeight * 4;
singleFaceSize += impl::getMipSize(header->highResImageFormat, mipWidth, mipHeight);
}
size_t offset = header->headerSize;
if ( header->lowResImageFormat != 0xFFFFFFFF ) {
offset += std::max<size_t>(1, (header->lowResImageWidth * header->lowResImageHeight) / 2);
offset += impl::getMipSize(header->lowResImageFormat, header->lowResImageWidth, header->lowResImageHeight);
}
size_t remainingBytes = buffer.size() - offset;
size_t bytesPerFace = singleFaceSize * numFrames;
int actualFaces = bytesPerFace > 0 ? (remainingBytes / bytesPerFace) : 1;
if ( actualFaces == 6 || actualFaces == 7 ) {
isCubemap = true;
}
int numFaces = 1;
if ( isCubemap ) {
if ( actualFaces >= 6 ) {
numFaces = actualFaces;
} else {
isCubemap = false;
numFaces = 1;
}
int numFrames = std::max<int>(1, header->frames);
if ( singleFaceSize > 0 && numFrames > 0 ) {
numFaces = (buffer.size() - offset) / (singleFaceSize * numFrames);
}
bool isHDR = (header->highResImageFormat == impl::IMAGE_FORMAT_RGBA16161616F);
bool isCubemap = (header->flags & impl::TEXTUREFLAGS_ENVMAP) != 0 && numFaces >= 6;
for ( int mip = header->mipmapCount - 1; mip > 0; --mip ) {
int mipWidth = std::max(1, header->width >> mip);
int mipHeight = std::max(1, header->height >> mip);
int blocksX = (mipWidth + 3) / 4;
int blocksY = (mipHeight + 3) / 4;
size_t mipSize = 0;
if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT1 ) mipSize = blocksX * blocksY * 8;
else if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT5 ) mipSize = blocksX * blocksY * 16;
else if ( header->highResImageFormat == impl::IMAGE_FORMAT_BGRA8888 ) mipSize = mipWidth * mipHeight * 4;
offset += mipSize * numFrames * numFaces;
offset += impl::getMipSize(header->highResImageFormat, mipWidth, mipHeight) * numFrames * numFaces;
}
int outputFaces = isCubemap ? 6 : 1;
int bytesPerPixel = isHDR ? 8 : 4;
image.size = { header->width, header->height * outputFaces };
image.channels = 4;
image.bpp = 8 * 4;
image.pixels.resize( header->width * header->height * 4 * outputFaces );
image.bpp = 8 * bytesPerPixel;
image.layers = numFrames;
image.format = isHDR ? uf::renderer::enums::Format::R16G16B16A16_SFLOAT : uf::renderer::enums::Format::R8G8B8A8_UNORM;
image.pixels.resize( header->width * header->height * bytesPerPixel * outputFaces * numFrames );
int faceSizePixels = header->width * header->height;
int faceSize = header->width * header->height;
int blocksX = (header->width + 3) / 4;
int blocksY = (header->height + 3) / 4;
const int faceMap[6] = { 4, 5, 0, 1, 2, 3 };
const int faceModes[6] = { 2, 0, 6, 1, 1, 3 };
for ( int face = 0; face < outputFaces; ++face ) {
const uint8_t* data = buffer.data() + offset;
int mappedFace = (isCubemap && face < 6) ? faceMap[face] : face;
uint8_t* outPixels = image.pixels.data() + (mappedFace * faceSizePixels * 4);
for ( int frame = 0; frame < numFrames; ++frame ) {
for ( int face = 0; face < outputFaces; ++face ) {
const uint8_t* data = buffer.data() + offset;
int mappedFace = (isCubemap && face < 6) ? faceMap[face] : face;
size_t outOffset = (frame * outputFaces + mappedFace) * faceSize * bytesPerPixel;
uint8_t* outPixels = image.pixels.data() + outOffset;
if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT1 ) {
for ( int by = 0; by < blocksY; ++by ) {
for ( int bx = 0; bx < blocksX; ++bx ) {
impl::decompressDXT1Block(data + (by * blocksX + bx) * 8, outPixels, bx * 4, by * 4, header->width, header->height);
}
}
offset += blocksX * blocksY * 8;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT5 ) {
for ( int by = 0; by < blocksY; ++by) {
for ( int bx = 0; bx < blocksX; ++bx) {
impl::decompressDXT5Block(data + (by * blocksX + bx) * 16, outPixels, bx * 4, by * 4, header->width, header->height);
}
}
offset += blocksX * blocksY * 16;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_BGRA8888 ) {
for ( auto i = 0; i < faceSizePixels; ++i ) {
outPixels[i * 4 + 0] = data[i * 4 + 2];
outPixels[i * 4 + 1] = data[i * 4 + 1];
outPixels[i * 4 + 2] = data[i * 4 + 0];
outPixels[i * 4 + 3] = data[i * 4 + 3];
}
offset += faceSizePixels * 4;
}
if ( isCubemap ) {
int mode = faceModes[mappedFace];
if ( mode == 0 ) continue;
uf::stl::vector<uint8_t> temp(faceSizePixels * 4);
memcpy(temp.data(), outPixels, faceSizePixels * 4);
int size = header->width;
int max = size - 1;
for ( int y = 0; y < size; ++y ) {
for ( int x = 0; x < size; ++x ) {
int srcX = x;
int srcY = y;
switch ( mode ) {
case 1: srcX = y; srcY = max - x; break; // 90 CW
case 2: srcX = max - x; srcY = max - y; break; // 180
case 3: srcX = max - y; srcY = x; break; // 90 CCW
case 4: srcX = max - x; srcY = y; break; // Flip Horizontal
case 5: srcX = x; srcY = max - y; break; // Flip Vertical
case 6: srcX = y; srcY = x; break; // Transpose (Diagonal Flip)
case 7: srcX = max - y; srcY = max - x; break; // Anti-Transpose
if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT1 ) {
for ( int by = 0; by < blocksY; ++by ) {
for ( int bx = 0; bx < blocksX; ++bx ) {
impl::decompressDXT1Block(data + (by * blocksX + bx) * 8, outPixels, bx * 4, by * 4, header->width, header->height);
}
}
offset += blocksX * blocksY * 8;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT3 ) {
for ( int by = 0; by < blocksY; ++by) {
for ( int bx = 0; bx < blocksX; ++bx) {
impl::decompressDXT3Block(data + (by * blocksX + bx) * 16, outPixels, bx * 4, by * 4, header->width, header->height);
}
}
offset += blocksX * blocksY * 16;
int srcIdx = (srcY * size + srcX) * 4;
int dstIdx = (y * size + x) * 4;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_DXT5 ) {
for ( int by = 0; by < blocksY; ++by) {
for ( int bx = 0; bx < blocksX; ++bx) {
impl::decompressDXT5Block(data + (by * blocksX + bx) * 16, outPixels, bx * 4, by * 4, header->width, header->height);
}
}
offset += blocksX * blocksY * 16;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_BGRX8888 || header->highResImageFormat == impl::IMAGE_FORMAT_RGBA8888 ) {
bool isRgba = (header->highResImageFormat == impl::IMAGE_FORMAT_RGBA8888);
for ( auto i = 0; i < faceSize; ++i ) {
outPixels[i * 4 + 0] = data[i * 4 + (isRgba ? 0 : 2)];
outPixels[i * 4 + 1] = data[i * 4 + 1];
outPixels[i * 4 + 2] = data[i * 4 + (isRgba ? 2 : 0)];
outPixels[i * 4 + 3] = isRgba ? data[i * 4 + 3] : 255;
}
offset += faceSize * 4;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_BGR888 ) {
for ( auto i = 0; i < faceSize; ++i ) {
outPixels[i * 4 + 0] = data[i * 3 + 2];
outPixels[i * 4 + 1] = data[i * 3 + 1];
outPixels[i * 4 + 2] = data[i * 3 + 0];
outPixels[i * 4 + 3] = 255;
}
offset += faceSize * 3;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_BGRA8888 ) {
for ( auto i = 0; i < faceSize; ++i ) {
outPixels[i * 4 + 0] = data[i * 4 + 2];
outPixels[i * 4 + 1] = data[i * 4 + 1];
outPixels[i * 4 + 2] = data[i * 4 + 0];
outPixels[i * 4 + 3] = data[i * 4 + 3];
}
offset += faceSize * 4;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_RGB888 ) {
for ( auto i = 0; i < faceSize; ++i ) {
outPixels[i * 4 + 0] = data[i * 3 + 0];
outPixels[i * 4 + 1] = data[i * 3 + 1];
outPixels[i * 4 + 2] = data[i * 3 + 2];
outPixels[i * 4 + 3] = 255;
}
offset += faceSize * 3;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_BGR565 ) {
for ( auto i = 0; i < faceSize; ++i ) {
uint16_t color = *(const uint16_t*)(data + i * 2);
impl::decodeRGB565(color, outPixels[i * 4 + 0], outPixels[i * 4 + 1], outPixels[i * 4 + 2]);
outPixels[i * 4 + 3] = 255;
}
offset += faceSize * 2;
} else if ( header->highResImageFormat == impl::IMAGE_FORMAT_RGBA16161616F ) {
std::memcpy(outPixels, data, faceSize * 8);
offset += faceSize * 8;
} else {
UF_MSG_ERROR("VTF '{}' has unimplemented format: 0x{:x}", filename, header->highResImageFormat );
}
outPixels[dstIdx + 0] = temp[srcIdx + 0];
outPixels[dstIdx + 1] = temp[srcIdx + 1];
outPixels[dstIdx + 2] = temp[srcIdx + 2];
outPixels[dstIdx + 3] = temp[srcIdx + 3];
if ( isCubemap ) {
int mode = faceModes[mappedFace];
if ( mode == 0 ) continue;
uf::stl::vector<uint8_t> temp(faceSize * bytesPerPixel);
std::memcpy(temp.data(), outPixels, faceSize * bytesPerPixel);
int size = header->width;
int max = size - 1;
for ( int y = 0; y < size; ++y ) {
for ( int x = 0; x < size; ++x ) {
int srcX = x, srcY = y;
switch ( mode ) {
case 1: srcX = y; srcY = max - x; break; // 90 CW
case 2: srcX = max - x; srcY = max - y; break; // 180
case 3: srcX = max - y; srcY = x; break; // 90 CCW
case 4: srcX = max - x; srcY = y; break; // Flip Horizontal
case 5: srcX = x; srcY = max - y; break; // Flip Vertical
case 6: srcX = y; srcY = x; break; // Transpose
case 7: srcX = max - y; srcY = max - x; break; // Anti-Transpose
}
int srcIdx = (srcY * size + srcX) * bytesPerPixel;
int dstIdx = (y * size + x) * bytesPerPixel;
std::memcpy(&outPixels[dstIdx], &temp[srcIdx], bytesPerPixel);
}
}
}
}
}
if ( (header->flags & impl::TEXTUREFLAGS_NORMAL) != 0 && header->highResImageFormat == impl::IMAGE_FORMAT_DXT5 ) {
size_t pixelCount = image.pixels.size() / 4;
for ( size_t i = 0; i < pixelCount; ++i ) {
uint8_t& r = image.pixels[i * 4 + 0];
uint8_t& g = image.pixels[i * 4 + 1];
uint8_t& b = image.pixels[i * 4 + 2];
uint8_t& a = image.pixels[i * 4 + 3];
float x = (a / 255.0f) * 2.0f - 1.0f;
float y = (g / 255.0f) * 2.0f - 1.0f;
y = -y;
float z = std::sqrt(std::max(1.0f - (x * x + y * y), 0.0f));
r = (uint8_t)((x * 0.5f + 0.5f) * 255.0f);
g = (uint8_t)((y * 0.5f + 0.5f) * 255.0f);
b = (uint8_t)((z * 0.5f + 0.5f) * 255.0f);
a = 255;
}
}
return true;
}

View File

@ -405,59 +405,41 @@ void ext::vulkan::Texture::loadFromImage(
VkImageLayout layout,
VkImageCreateFlags flags
) {
/*
switch ( format ) {
case enums::Format::R8_SRGB:
case enums::Format::R8G8_SRGB:
case enums::Format::R8G8B8_SRGB:
case enums::Format::R8G8B8A8_SRGB:
srgb = true;
break;
}
*/
switch ( image.getChannels() ) {
// R
case 1:
switch ( image.getBpp() ) {
case 8:
format = srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
break;
default:
UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() );
break;
case 8: format = srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM; break;
case 16: format = VK_FORMAT_R16_SFLOAT; break; // Half-float
case 32: format = VK_FORMAT_R32_SFLOAT; break; // Full-float
default: UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() ); break;
}
break;
// RG
case 2:
switch ( image.getBpp() ) {
case 16:
format = srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM;
break;
default:
UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() );
break;
case 16: format = srgb ? VK_FORMAT_R8G8_SRGB : VK_FORMAT_R8G8_UNORM; break;
case 32: format = VK_FORMAT_R16G16_SFLOAT; break;
case 64: format = VK_FORMAT_R32G32_SFLOAT; break;
default: UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() ); break;
}
break;
// RGB
case 3:
switch ( image.getBpp() ) {
case 24:
format = srgb ? VK_FORMAT_R8G8B8_SRGB : VK_FORMAT_R8G8B8_UNORM;
break;
default:
UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() );
break;
case 24: format = srgb ? VK_FORMAT_R8G8B8_SRGB : VK_FORMAT_R8G8B8_UNORM; break;
case 48: format = VK_FORMAT_R16G16B16_SFLOAT; break;
case 96: format = VK_FORMAT_R32G32B32_SFLOAT; break;
default: UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() ); break;
}
break;
// RGBA
case 4:
switch ( image.getBpp() ) {
case 32:
format = srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
break;
default:
UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() );
break;
case 32: format = srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; break;
case 64: format = VK_FORMAT_R16G16B16A16_SFLOAT; break; // 16-bit HDR
case 128: format = VK_FORMAT_R32G32B32A32_SFLOAT; break; // 32-bit HDR
default: UF_EXCEPTION("Vulkan error: unsupported BPP of {}", image.getBpp() ); break;
}
break;
default:
@ -465,9 +447,6 @@ void ext::vulkan::Texture::loadFromImage(
break;
}
// convert to power of two
//image.padToPowerOfTwo();
this->fromBuffers(
(void*) image.getPixelsPtr(),
image.getPixels().size(),

View File

@ -20,6 +20,32 @@ namespace {
auto* bytes = static_cast<uint8_t*>(data);
buffer->insert(buffer->end(), bytes, bytes + size);
}
inline uint8_t halfTo8Bit(uint16_t h) {
uint32_t exp = (h >> 10) & 0x1F;
uint32_t mant = h & 0x3FF;
if (exp == 0) return 0;
if (exp == 31) return 255;
exp = exp + (127 - 15);
uint32_t f = (exp << 23) | (mant << 13);
float val;
std::memcpy(&val, &f, sizeof(float));
return (uint8_t)(std::min(std::max(val, 0.0f), 1.0f) * 255.0f);
}
inline float halfToFloat(uint16_t h) {
uint32_t exp = (h >> 10) & 0x1F;
uint32_t mant = h & 0x3FF;
if (exp == 0 && mant == 0) return 0.0f;
exp = exp + (127 - 15);
uint32_t f = (exp << 23) | (mant << 13);
float val;
std::memcpy(&val, &f, sizeof(float));
return val;
}
}
namespace impl {
@ -142,10 +168,10 @@ bool uf::image::open( pod::Image& image, const uf::stl::vector<uint8_t>& buffer,
// palette data: buffer.data() + sizeof(header) + header.size
bool twiddled = (header.type & (1 << 26)) < 1;
bool compressed = (header.type & (1 << 30)) > 0;
bool mipmapped = (header.type & (1 << 31)) > 0;
bool strided = (header.type & (1 << 25)) > 0;
bool twiddled = (header.type & (1 << 26)) < 1;
bool compressed = (header.type & (1 << 30)) > 0;
bool mipmapped = (header.type & (1 << 31)) > 0;
bool strided = (header.type & (1 << 25)) > 0;
uint32_t format = (header.type >> 27) & 0b111;
width = header.width;
height = header.height;
@ -173,22 +199,40 @@ bool uf::image::open( pod::Image& image, const uf::stl::vector<uint8_t>& buffer,
#endif
{
stbi_set_flip_vertically_on_load( flip );
uint8_t* stbi_pixels = stbi_load_from_memory( buffer.data(), buffer.size(), &width, &height, &channelsDud, STBI_rgb_alpha );
if ( stbi_is_hdr_from_memory( buffer.data(), buffer.size() ) ) {
float* stbi_pixels = stbi_loadf_from_memory( buffer.data(), buffer.size(), &width, &height, &channelsDud, STBI_rgb_alpha );
if ( !stbi_pixels ) {
UF_EXCEPTION("Image error: stb_image failed to decode HDR buffer");
return false;
}
if ( !stbi_pixels ) {
UF_EXCEPTION("Image error: stb_image failed to decode buffer");
return false;
size_t len = width * height * 4 * sizeof(float);
image.pixels.resize( len );
std::memcpy( image.pixels.data(), stbi_pixels, len );
stbi_image_free(stbi_pixels);
bpp = 32;
channels = 4;
} else {
uint8_t* stbi_pixels = stbi_load_from_memory( buffer.data(), buffer.size(), &width, &height, &channelsDud, STBI_rgb_alpha );
if ( !stbi_pixels ) {
UF_EXCEPTION("Image error: stb_image failed to decode buffer");
return false;
}
size_t len = width * height * 4;
image.pixels.resize( len );
std::memcpy( image.pixels.data(), stbi_pixels, len );
stbi_image_free(stbi_pixels);
bpp = 8;
channels = 4;
}
size_t len = width * height * channels;
image.pixels.resize( len );
memcpy( image.pixels.data(), stbi_pixels, len );
stbi_image_free(stbi_pixels);
}
image.size.x = width;
image.size.y = height;
image.size.y = height / MAX(1, image.layers);
image.bpp = bpp * channels;
image.channels = channels;
return true;
@ -235,12 +279,25 @@ void uf::image::save( const pod::Image& image, uf::stl::vector<uint8_t>& buffer,
if ( image.pixels.empty() ) return;
uint w = image.size.x;
uint h = image.size.y;
uint h = image.pixels.size() / (w * (image.bpp / 8));
auto* pixels = &image.pixels[0];
uf::stl::string extension = image.filename.empty() ? "png" : uf::io::extension( image.filename );
stbi_flip_vertically_on_write(flip);
if ( extension == "png" ) {
if ( extension == "hdr" ) {
if ( image.bpp == 128 ) {
stbi_write_hdr_to_func(stbi_buffer_write_func, &buffer, w, h, image.channels, (const float*)pixels);
} else if ( image.bpp == 64 ) {
size_t pixelCount = w * h * image.channels;
uf::stl::vector<float> temp32(pixelCount);
const uint16_t* halfPixels = (const uint16_t*)pixels;
for ( size_t i = 0; i < pixelCount; ++i ) temp32[i] = ::halfToFloat(halfPixels[i]);
stbi_write_hdr_to_func(stbi_buffer_write_func, &buffer, w, h, image.channels, temp32.data());
} else {
UF_MSG_ERROR("Cannot save 8-bit image natively as HDR.");
}
} else if ( extension == "png" ) {
stbi_write_png_to_func(stbi_buffer_write_func, &buffer, w, h, image.channels, pixels, w * image.channels);
} else if ( extension == "jpg" || extension == "jpeg" ) {
stbi_write_jpg_to_func(stbi_buffer_write_func, &buffer, w, h, image.channels, pixels, 90); // quality
@ -252,6 +309,11 @@ void uf::image::save( const pod::Image& image, std::ostream& stream ) {
}
void uf::image::layers( pod::Image& image, size_t layers ) {
if ( image.layers != layers ) image.size.y /= MAX(1, layers);
image.layers = layers;
}
pod::Image::pixel_t uf::image::at( pod::Image& image, const pod::Vector2ui& at ) {
size_t i = at.x * image.channels + image.size.x * image.channels * at.y;
return {
@ -414,66 +476,7 @@ pod::Image uf::image::scale( const pod::Image& image, const pod::Vector2ui& size
if ( filter == "linear" || filter == "bilinear" ) return impl::scaleBilinear( image, size );\
UF_EXCEPTION("unrecognized scale filter: {}", filter );
}
/*
uf::Image::Image() {
size = {0,0};
bpp = 8;
channels = 4;
format = 0;
}
uf::Image::Image(const pod::Vector2ui& s) {
size = s;
bpp = 8;
channels = 4;
format = 0;
pixels.resize(size.x * size.y * channels);
}
uf::Image::Image( pod::Image::container_t&& move, const pod::Vector2ui& s ) {
pixels = std::move( move );
size = s;
bpp = 8;
channels = 4;
format = 0;
}
uf::Image::Image( const pod::Image::container_t& copy, const pod::Vector2ui& s ) {
pixels = copy;
size = s;
bpp = 8;
channels = 4;
format = 0;
}
uf::Image::Image( const uf::Image& copy ) {
this->copy( copy );
}
uf::Image::Image( uf::Image&& move ) noexcept {
//this->move( move );
pixels = std::move( move.pixels );
size = move.size;
bpp = move.bpp;
channels = move.channels;
format = move.format;
}
uf::Image& uf::Image::operator=( const uf::Image& copy ) {
this->copy( copy );
return *this;
}
uf::Image& uf::Image::operator=( uf::Image&& move ) noexcept {
//this->move( move );
pixels = std::move( move.pixels );
size = move.size;
bpp = move.bpp;
channels = move.channels;
format = move.format;
return *this;
}
*/
uf::stl::string uf::Image::getFilename() const {
return this->filename;
}
@ -573,6 +576,9 @@ size_t uf::Image::getFormat() const {
uf::stl::string uf::Image::getHash() const {
return uf::image::hash( *this );
}
void uf::Image::setLayers( size_t layers ) {
return uf::image::layers( *this, layers );
}
pod::Image::pixel_t uf::Image::at( const pod::Vector2ui& at ) {
return uf::image::at( *this, at );
}