Skip to content

Commit

Permalink
Merge pull request #16 from BredaUniversityGames/dds-cleanup
Browse files Browse the repository at this point in the history
Cleaned up DDS texture loading implementation
  • Loading branch information
Contingencyy authored Feb 17, 2024
2 parents b09d6c6 + 806bdcd commit 8ec3af3
Show file tree
Hide file tree
Showing 21 changed files with 823 additions and 591 deletions.
45 changes: 45 additions & 0 deletions PNGToDDS.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import os
import sys

dirName = sys.argv[1] # Get the name of the folder passed to this script
print(f'Creating batch script for directory {dirName}')

filesInDir = os.listdir(dirName) # Get all files in the directory
filesInDir = [f for f in filesInDir if f.lower().endswith('png')] # Get only PNG files
filesInDir = [os.path.join(dirName, f) for f in filesInDir] # Get absolute paths
filesInDir = [f for f in filesInDir if os.path.isfile(f)] # Ignore directories

outScript = open('DXX_Raytracer_Convert_PNG_To_DDS.nvtt', 'w') # Start a new script

baseColorTexturesInDir = [f for f in filesInDir if f.find("basecolor")]

for file in baseColorTexturesInDir:
newFileName = file.rpartition('.')[0] + '.dds' # Replace .png with .dds
outScript.write(f'{file} --format bc7 --dx10 --mips --output {newFileName}\n')

metallicTexturesInDir = [f for f in filesInDir if f.find("metallic")]

for file in metallicTexturesInDir:
newFileName = file.rpartition('.')[0] + '.dds' # Replace .png with .dds
outScript.write(f'{file} --format bc4 --dx10 --mips --output {newFileName}\n')

roughnessTexturesInDir = [f for f in filesInDir if f.find("roughness")]

for file in roughnessTexturesInDir:
newFileName = file.rpartition('.')[0] + '.dds' # Replace .png with .dds
outScript.write(f'{file} --format bc4 --dx10 --mips --output {newFileName}\n')

normalTexturesInDir = [f for f in filesInDir if f.find("normal")]

for file in normalTexturesInDir:
newFileName = file.rpartition('.')[0] + '.dds' # Replace .png with .dds
outScript.write(f'{file} --format bc7 --dx10 --mips --output {newFileName}\n')

emissiveTexturesInDir = [f for f in filesInDir if f.find("emissive")]

for file in emissiveTexturesInDir:
newFileName = file.rpartition('.')[0] + '.dds' # Replace .png with .dds
outScript.write(f'{file} --format bc7 --export-pre-alpha --dx10 --mips --output {newFileName}\n')

outScript.close()
print('Done')
4 changes: 3 additions & 1 deletion RT/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ add_library(RT_CORE INTERFACE
"SlotMap.hpp"
"Config.h"
"String.h"
"Hash.h" )
"Hash.h"
"FileIO.h")

target_sources(RT_CORE INTERFACE
"${CMAKE_CURRENT_LIST_DIR}/Config.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Arena.c"
"${CMAKE_CURRENT_LIST_DIR}/FileIO.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Common.c"
"${CMAKE_CURRENT_LIST_DIR}/String.c"
"${CMAKE_CURRENT_LIST_DIR}/VirtualMemory.c"
Expand Down
13 changes: 13 additions & 0 deletions RT/Core/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ RT_API double RT_SecondsElapsed(RT_HighResTime start, RT_HighResTime end);
#define RT_SLL_PUSH(head, node) ((node)->next = (head), (head) = (node))
#define RT_SLL_POP(head) head; do { (head) = (head)->next; } while(0)

#define RT_IS_POW2(x) (((x) & ((x) - 1)) == 0)

static inline uint32_t RT_U32Log2(uint32_t x)
{
uint32_t result = 0;
while (x)
{
result += 1;
x = x >> 1;
}
return result;
}

#define RT_DLL_PUSH_BACK(first, last, node) \
do { \
(node)->prev = (last); \
Expand Down
43 changes: 43 additions & 0 deletions RT/Core/FileIO.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "FileIO.h"

#include "Core/Arena.h"
#include "Core/String.h"

#include <stdio.h>

bool RT_ReadEntireFile(RT_Arena *arena, RT_String path, RT_String *result)
{
const char *path_c = RT_CopyStringNullTerm(&g_thread_arena, path);

FILE *f = fopen(path_c, "rb");

if (!f)
{
// fprintf(stderr, "Failed to open file '%s'.\n", path_c);
return false;
}

defer { fclose(f); };

fseek(f, 0, SEEK_END);
size_t file_size = ftell(f);
fseek(f, 0, SEEK_SET);

RT_String file;
file.bytes = (char *)RT_ArenaAllocNoZero(arena, file_size + 1, 16);
file.count = file_size;

size_t bytes_read = fread(file.bytes, 1, file_size, f);

if (bytes_read != file_size)
{
return false;
}

// Null terminate for good measure
file.bytes[file.count] = 0;

*result = file;

return true;
}
12 changes: 12 additions & 0 deletions RT/Core/FileIO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "ApiTypes.h"

// The descent code likes to mess with padding, so public headers should use pragma pack to
// restore expected packing!
#pragma pack(push, 8)

RT_API bool RT_ReadEntireFile(RT_Arena *arena, RT_String path, RT_String *result);

// Don't forget to pop.
#pragma pack(pop)
63 changes: 63 additions & 0 deletions RT/Core/String.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ static inline RT_String RT_CopyString(RT_Arena *arena, RT_String string)
return result;
}

static inline const char *RT_CopyStringNullTerm(RT_Arena *arena, RT_String string)
{
char *result = (char *)RT_ArenaCopyArray(arena, string.bytes, string.count + 1);
result[string.count + 1] = 0;
return result;
}

typedef struct RT_ParseFloatResult
{
bool success;
Expand All @@ -111,6 +118,11 @@ typedef struct RT_ParseIntResult

RT_API RT_ParseIntResult RT_ParseInt(RT_String string, int base);

static inline char RT_CharToLower(char c)
{
return c >= 'A' && c <= 'Z' ? c + 'a' - 'A' : c;
}

static inline bool RT_StringsAreEqual(RT_String a, RT_String b)
{
bool result = a.count == b.count;
Expand All @@ -130,6 +142,57 @@ static inline bool RT_StringsAreEqual(RT_String a, RT_String b)
return result;
}

static inline bool RT_StringsAreEqualNoCase(RT_String a, RT_String b)
{
bool result = a.count == b.count;

if (result)
{
for (size_t i = 0; i < a.count; i++)
{
if (RT_CharToLower(a.bytes[i]) != RT_CharToLower(b.bytes[i]))
{
result = false;
break;
}
}
}

return result;
}

static inline RT_String RT_Substring(RT_String string, size_t start, size_t count)
{
start = RT_MIN(start, string.count);

if (count > start - string.count)
{
count = start - string.count;
}

RT_String result;
result.bytes = string.bytes + start;
result.count = count;
return result;
}

// Returns extension including dot
// For an extension like .tar.gz it will only return '.gz'.
static inline RT_String RT_StringFindExtension(RT_String string)
{
size_t last_dot = RT_String_NPOS;

for (size_t i = 0; i < string.count; i++)
{
if (string.bytes[i] == '.')
{
last_dot = i;
}
}

return RT_Substring(string, last_dot, RT_String_NPOS);
}

static inline size_t RT_StringFindChar(RT_String string, char c)
{
for (size_t i = 0; i < string.count; i++)
Expand Down
10 changes: 5 additions & 5 deletions RT/RTgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -801,11 +801,11 @@ static void RT_GetPolyData(RT_TriangleBuffer *buf,
void RT_InitglTFModels(void)
{
g_rt_cockpit_settings.cockpit_hud_texture = RT_UploadTexture(&(RT_UploadTextureParams) {
.width = 1024,
.height = 1024,
.name = "Cockpit UI",
.format = RT_TextureFormat_SRGBA8,
.pixels = RT_ArenaAllocArray(&g_thread_arena, 1024 * 1024, uint32_t)
.image.width = 1024,
.image.height = 1024,
.image.format = RT_TextureFormat_RGBA8_SRGB,
.image.pixels = RT_ArenaAllocArray(&g_thread_arena, 1024 * 1024, uint32_t),
.name = "Cockpit UI",
});

RT_Material cockpit_material = { 0 };
Expand Down
84 changes: 39 additions & 45 deletions RT/RTmaterials.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@

RT_TextureFormat g_rt_material_texture_slot_formats[RT_MaterialTextureSlot_COUNT] =
{
[RT_MaterialTextureSlot_Albedo] = RT_TextureFormat_SRGBA8,
// NOTE(daniel): These formats are kind of misleading with DDS textures, for them we look at them just to know if it's sRGB or not.
[RT_MaterialTextureSlot_Albedo] = RT_TextureFormat_RGBA8_SRGB,
[RT_MaterialTextureSlot_Normal] = RT_TextureFormat_RGBA8,
[RT_MaterialTextureSlot_Metalness] = RT_TextureFormat_R8,
[RT_MaterialTextureSlot_Roughness] = RT_TextureFormat_R8,
[RT_MaterialTextureSlot_Emissive] = RT_TextureFormat_SRGBA8, // TODO(daniel): Double check emissive textures should be sRGB encoded.
[RT_MaterialTextureSlot_Emissive] = RT_TextureFormat_RGBA8_SRGB, // TODO(daniel): Double check emissive textures should be sRGB encoded.
};

char *g_rt_texture_slot_names[RT_MaterialTextureSlot_COUNT] =
Expand Down Expand Up @@ -160,75 +161,68 @@ static int RT_LoadMaterialTexturesFromPaths(uint16_t bm_index, RT_Material *mate
{
if (load_mask & RT_BITN(i))
{

// first try loading the compressed dds version of the file if it exists
bool ddsLoaded = false;
bool dds_loaded = false;
RT_ArenaMemoryScope(&g_thread_arena)
{
char* ddsFile = RT_ArenaPrintF(&g_thread_arena, "assets/textures/%s.dds", paths->textures[i]);

uint8_t* ddsData = NULL; // this is pointer to whole file including header
const struct DDS_HEADER* header = NULL; // pointer to the header (in same buffer as ddsData)
const uint8_t* bitData = NULL; // pointer to the compressed pixel data (in same buffer as ddsData)
size_t bitSize = 0; // size of the compressed pixel data

bool fileLoaded = RT_LoadDDSImageFromDisk(&g_thread_arena, ddsFile, &ddsData, &header, &bitData, &bitSize);

if (fileLoaded && ddsData)
RT_TextureFormat format = g_rt_material_texture_slot_formats[i];
bool is_srgb = (format == RT_TextureFormat_RGBA8_SRGB);

char* dds_file = RT_ArenaPrintF(&g_thread_arena, "assets/textures/%s.dds", paths->textures[i]);

// TODO(daniel): It's dumb that RT_ArenaPrintF doesn't return an RT_String
RT_Image image = RT_LoadDDSFromDisk(&g_thread_arena, RT_StringFromCString(dds_file));

if (is_srgb)
{
image.format = RT_TextureFormatToSRGB(image.format);
}

if (image.pixels)
{
ddsLoaded = true;

material->textures[i] = RT_UploadTextureDDS(&(RT_UploadTextureParamsDDS) {
.header = header,
.ddsData = ddsData,
.bitData = bitData,
.bitSize = bitSize,
.sRGB = i == 0 || i == 4, // set sRGB to true if basecolor or emissive
.name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:%s (source: %s)",
bm_index, g_rt_texture_slot_names[i], ddsFile)
dds_loaded = true;

material->textures[i] = RT_UploadTexture(&(RT_UploadTextureParams) {
.image = image,
.name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:%s (source: %s)", bm_index, g_rt_texture_slot_names[i], dds_file),
});

textures_reloaded += 1;
}

}

if (!ddsLoaded) // dds file not loaded try png
if (!dds_loaded) // dds file not loaded try png
{
RT_ArenaMemoryScope(&g_thread_arena)
{
char* file = RT_ArenaPrintF(&g_thread_arena, "assets/textures/%s.png", paths->textures[i]);

RT_TextureFormat format = g_rt_material_texture_slot_formats[i];

int bpp = g_rt_texture_format_bpp[format];
bool is_srgb = format == RT_TextureFormat_RGBA8_SRGB;
int bpp = g_rt_texture_format_bpp[format];

int w = 0, h = 0, c = 0;
unsigned char* pixels = RT_LoadImageFromDisk(&g_thread_arena, file, &w, &h, &c, bpp);
RT_Image image = RT_LoadImageFromDisk(&g_thread_arena, file, bpp, is_srgb);

if (i == RT_MaterialTextureSlot_Emissive)
// NOTE(daniel): Mildly sketchy code, that format check is checking it's an uncompressed format.
if (i == RT_MaterialTextureSlot_Emissive && image.format == format)
{
// Premultiply emissive by alpha to avoid white transparent backgrounds from showing...

uint32_t* at = (uint32_t*)pixels;
for (size_t i = 0; i < w * h; i++)
uint32_t* at = (uint32_t*)image.pixels;
for (size_t i = 0; i < image.width * image.height; i++)
{
RT_Vec4 color = RT_UnpackRGBA(*at);
color.xyz = RT_Vec3Muls(color.xyz, color.w);
*at++ = RT_PackRGBA(color);
}
}

if (pixels)
if (image.pixels)
{
material->textures[i] = RT_UploadTexture(&(RT_UploadTextureParams) {
.format = format,
.width = w,
.height = h,
.pixels = pixels,
.pitch = bpp * w,
.name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:%s (source: %s)",
bm_index, g_rt_texture_slot_names[i], file),
.image = image,
.name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:%s (source: %s)", bm_index, g_rt_texture_slot_names[i], file),
});

textures_reloaded += 1;
Expand Down Expand Up @@ -334,11 +328,11 @@ void RT_InitAllBitmaps(void)
uint32_t *pixels = dx12_load_bitmap_pixel_data(&g_thread_arena, bitmap);

material->albedo_texture = RT_UploadTexture(&(RT_UploadTextureParams) {
.width = bitmap->bm_w,
.height = bitmap->bm_h,
.pixels = pixels,
.name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:basecolor (original)", bm_index),
.format = g_rt_material_texture_slot_formats[RT_MaterialTextureSlot_Albedo],
.image.width = bitmap->bm_w,
.image.height = bitmap->bm_h,
.image.pixels = pixels,
.image.format = g_rt_material_texture_slot_formats[RT_MaterialTextureSlot_Albedo],
.name = RT_ArenaPrintF(&g_thread_arena, "Game Texture %hu:basecolor (original)", bm_index),
});

#ifdef RT_DUMP_GAME_BITMAPS
Expand Down
Loading

0 comments on commit 8ec3af3

Please sign in to comment.