Skip to content

Commit

Permalink
Added HDR support
Browse files Browse the repository at this point in the history
Now produces an HDR version of the VMT file.
  • Loading branch information
bottiger1 committed May 23, 2021
1 parent 891ba49 commit 152bbb4
Show file tree
Hide file tree
Showing 8 changed files with 532 additions and 20 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,16 @@ Two files will be produced

* theskybox_cubemap.vtf
* theskybox_cubemap.vmt
* theskybox_cubemap.hdr.vmt

Move these files to **materials/cubemap_skyboxes** or edit **theskybox_cubemap.vmt** to use a different path.

Now you can create a **func_brush** with this texture around your **sky_camera** and toggle it on and off whenever you want.

You can remove the hdr.vmt file if you are compiling in LDR only. Otherwise if you compile in HDR, anyone using full HDR will
see a blinding white light instead. The HDR file is large, but you can get compression ratios of 10x or more when the bsp is
compressed by repacking or bz2.

## How to compile

The program was added as a Visual Studio 2019 solution to VTFLib. Open sln/vs2017/VTFLib.sln
Expand All @@ -54,6 +59,6 @@ The program was written by Bottiger @ skial.com. Thanks to Berke for discovering

## Licenses

The cubemaker program is licensed as BSD. VTFLib is LGPL. VTFCmd and VTFEdit are GPL.
The cubemaker program is licensed as BSD. VTFLib is LGPL. VTFCmd and VTFEdit are GPL. half.h/half.cpp is licensed as BSD by another author.

The cubemaker program does not rely on VTFCmd or VTFEdit, they are just included in the repository as they are bundled with VTFLib.
2 changes: 1 addition & 1 deletion VTFLib/VTFFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3538,7 +3538,7 @@ vlSingle sHDRLogAverageLuminance;

vlVoid ToFP16(vlUInt16& R, vlUInt16& G, vlUInt16& B, vlUInt16& A)
{

}

vlSingle ClampFP16(vlSingle sValue)
Expand Down
3 changes: 2 additions & 1 deletion sln/vs2017/VTFLib/VTFLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@
<OutDir>..\..\..\bin\$(Configuration)\$(Platform)\</OutDir>
<IntDir>$(Configuration)\$(Platform)\</IntDir>
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>true</RunCodeAnalysis>
<RunCodeAnalysis>false</RunCodeAnalysis>
<EnableMicrosoftCodeAnalysis>false</EnableMicrosoftCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>..\..\..\bin\$(Configuration)\$(Platform)\</OutDir>
Expand Down
128 changes: 111 additions & 17 deletions sln/vs2017/cubemaker/cubemaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <VTFFile.h>
#include <VTFLib.h>
#include "half.h"

const char* g_banner = "Skybox to Cubemap Maker by Bottiger skial.com\n\n";

Expand All @@ -16,12 +17,14 @@ All 6 of these VTF files must be in the same directory. Sometimes the dn VTF is
file with the same name, find the VTF it points to, copy and paste it into the same folder as the VMT
and give it the same name except with the extension VTF.
2 files will be created: theskybox_cubemap.vtf and theskybox_cubemap.vmt
3 files will be created: theskybox_cubemap.vtf, theskybox_cubemap.vmt, theskybox_cubemap.hdr.vmt
Edit theskybox_cubemap.vmt and change the path if needed. Then copy theskybox_cubemap.vtf and
theskybox_cubemap.vmt to that path.
Edit theskybox_cubemap.vmt and change the path if needed. Then copy the 3 files to that path.
You can now apply theskybox_cubemap to the faces around the sky_camera.
You can optionally delete the hdr.vmt if you are compiling LDR only. If you compile in HDR without this
file, then the material will be replaced with a blinding white one.
)";

const char* g_completed = R"(DONE
Expand All @@ -35,6 +38,69 @@ R"("WindowImposter"
"$envmap" "cubemap_skyboxes/%s_cubemap"
})";

struct CVTFFileUnprivate
{
SVTFHeader* Header;
vlUInt uiImageBufferSize;
vlByte* lpImageData;
};

#pragma pack(1)
struct RGBA16
{
uint16_t r;
uint16_t g;
uint16_t b;
uint16_t a;
};

struct BGRA8
{
unsigned char b;
unsigned char g;
unsigned char r;
unsigned char a;
};

// VTFLib does not properly convert these modes. we fully convert them here instead of the DLL so we don't have to deal with
// getting nvidia's library to compile

void ConvertImageToFloat(VTFLib::CVTFFile* vtf)
{
auto output_open = (CVTFFileUnprivate*)vtf;
auto ptr = (RGBA16*)output_open->lpImageData;
vlUInt pixels = vtf->GetHeight() * vtf->GetWidth() * vtf->GetFaceCount() * vtf->GetFrameCount();

for (vlUInt i = 0; i < pixels; i++)
{
const float factor = (float)(1.0 / 65535.0);
float fR = ptr[i].r * factor;
float fG = ptr[i].g * factor;
float fB = ptr[i].b * factor;
float fA = ptr[i].a * factor;

ptr[i].r = half::FLOAT16::ToFloat16Fast(fR).m_uiFormat;
ptr[i].g = half::FLOAT16::ToFloat16Fast(fG).m_uiFormat;
ptr[i].b = half::FLOAT16::ToFloat16Fast(fB).m_uiFormat;
ptr[i].a = half::FLOAT16::ToFloat16Fast(fA).m_uiFormat;
}
}

void ConvertImageToBGRA(VTFLib::CVTFFile* vtf)
{
auto output_open = (CVTFFileUnprivate*)vtf;
auto ptr = (BGRA8*)output_open->lpImageData;
vlUInt pixels = vtf->GetHeight() * vtf->GetWidth() * vtf->GetFaceCount() * vtf->GetFrameCount();
for (vlUInt i = 0; i < pixels; i++)
{
float divisor = 8.0f;
ptr[i].a = 0; // does nothing at 0 or 255
ptr[i].r = (unsigned char)((ptr[i].r + 0.5f) / divisor);
ptr[i].g = (unsigned char)((ptr[i].g + 0.5f) / divisor);
ptr[i].b = (unsigned char)((ptr[i].b + 0.5f) / divisor);
}
}

int EndsWith(const char* str, const char* suffix)
{
if (!str || !suffix)
Expand Down Expand Up @@ -141,7 +207,7 @@ int main(int argc, char* argv[])
}
}

auto output = VTFLib::CVTFFile();
auto cubemap = VTFLib::CVTFFile();
auto width = faces[0]->GetWidth();
auto height = faces[0]->GetHeight();

Expand All @@ -153,7 +219,7 @@ int main(int argc, char* argv[])
auto face_width = faces[i]->GetWidth();
auto face_height = faces[i]->GetHeight();
main_buffer[i] = (vlByte*)malloc(4 * face_height * face_width);
auto success = output.ConvertToRGBA8888(faces[i]->GetData(0, 0, 0, 0), main_buffer[i], face_width, face_height, faces[i]->GetFormat());
auto success = cubemap.ConvertToRGBA8888(faces[i]->GetData(0, 0, 0, 0), main_buffer[i], face_width, face_height, faces[i]->GetFormat());
if (!success)
{
printf("ConvertToRGBA8888 %s %s\n", g_faceorder[i], vlGetLastError());
Expand All @@ -174,7 +240,7 @@ int main(int argc, char* argv[])
{
filter = VTFMipmapFilter::MIPMAP_FILTER_MITCHELL;
}
success = output.Resize(main_buffer[i], resized, face_width, face_height, width, height, filter, VTFSharpenFilter::SHARPEN_FILTER_SHARPENSOFT);
success = cubemap.Resize(main_buffer[i], resized, face_width, face_height, width, height, filter, VTFSharpenFilter::SHARPEN_FILTER_SHARPENSOFT);
if (!success)
{
printf("resize failed: %s\n", vlGetLastError());
Expand All @@ -190,26 +256,26 @@ int main(int argc, char* argv[])
{
// make rt load before lf for this
case 0:
output.FlipImage(main_buffer[i], width, height);
cubemap.FlipImage(main_buffer[i], width, height);
Rotate90CW(main_buffer[i], width, height);
Rotate90CW(main_buffer[i], width, height);
Rotate90CW(main_buffer[i], width, height);
break;
case 1:
output.FlipImage(main_buffer[i], width, height);
cubemap.FlipImage(main_buffer[i], width, height);
Rotate90CW(main_buffer[i], width, height);
break;
case 2:
output.FlipImage(main_buffer[i], width, height);
cubemap.FlipImage(main_buffer[i], width, height);
break;
case 3:
output.MirrorImage(main_buffer[i], width, height);
cubemap.MirrorImage(main_buffer[i], width, height);
break;
case 4:
output.FlipImage(main_buffer[i], width, height);
cubemap.FlipImage(main_buffer[i], width, height);
break;
case 5:
output.MirrorImage(main_buffer[i], width, height);
cubemap.MirrorImage(main_buffer[i], width, height);
break;

/*
Expand Down Expand Up @@ -241,29 +307,57 @@ int main(int argc, char* argv[])
options.uiVersion[0] = 7;
options.uiVersion[1] = 4;
options.uiFlags = 0x0004 | 0x0008;
options.ImageFormat = VTFImageFormat::IMAGE_FORMAT_DXT5;
options.ImageFormat = VTFImageFormat::IMAGE_FORMAT_RGBA8888;
options.bSphereMap = true;
options.bThumbnail = true;

printf("Building cubemap\n");
bool success;
success = output.Create(width, height, 1, 6, 1, (vlByte**)&main_buffer, options);
success = cubemap.Create(width, height, 1, 6, 1, (vlByte**)&main_buffer, options);
if (!success)
{
printf("Create Error %s\n", vlGetLastError());
PressKeyToContinue();
std::terminate();
}


auto ldr = VTFLib::CVTFFile(cubemap, VTFImageFormat::IMAGE_FORMAT_DXT5);
char output_name[FILENAME_MAX];
snprintf(output_name, sizeof(output_name), "%s_cubemap.vtf", base);
success = output.Save(output_name);
success = ldr.Save(output_name);
if (!success)
{
printf("LDR Save Error %s\n", vlGetLastError());
PressKeyToContinue();
std::terminate();
}
ldr.Destroy();

auto hdr = VTFLib::CVTFFile(cubemap, VTFImageFormat::IMAGE_FORMAT_RGBA16161616F);
ConvertImageToFloat(&hdr);
snprintf(output_name, sizeof(output_name), "%s_cubemap.hdr.vtf", base);
success = hdr.Save(output_name);
if (!success)
{
printf("HDR Save Error %s\n", vlGetLastError());
PressKeyToContinue();
std::terminate();
}
hdr.Destroy();

/*
auto hdr_lq = VTFLib::CVTFFile(cubemap, VTFImageFormat::IMAGE_FORMAT_BGRA8888);
ConvertImageToBGRA(&hdr_lq);
snprintf(output_name, sizeof(output_name), "%s_cubemap.hdrlq.vtf", base);
success = hdr_lq.Save(output_name);
if (!success)
{
printf("Save Error %s\n", vlGetLastError());
printf("HDR LQ Save Error %s\n", vlGetLastError());
PressKeyToContinue();
std::terminate();
}
hdr_lq.Destroy();
*/

snprintf(output_name, sizeof(output_name), "%s_cubemap.vmt", base);
FILE* f;
Expand Down
4 changes: 4 additions & 0 deletions sln/vs2017/cubemaker/cubemaker.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,16 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="cubemaker.cpp" />
<ClCompile Include="half.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VTFLib\VTFLib.vcxproj">
<Project>{85ecfc39-0719-47b3-a90e-961e0f1750ca}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="half.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
Expand Down
8 changes: 8 additions & 0 deletions sln/vs2017/cubemaker/cubemaker.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,13 @@
<ClCompile Include="cubemaker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="half.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="half.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
Loading

0 comments on commit 152bbb4

Please sign in to comment.