Browse Source

First import of PS1 MDL/BSP tools project, long overdue

master
Nico de Poel 3 years ago
commit
8f82f467a0
  1. 12
      .gitignore
  2. BIN
      N64E1M1.bsp
  3. BIN
      N64E2M2.bsp
  4. 41
      PS1BSP.sln
  5. 153
      PS1BSP.vcxproj
  6. 30
      PS1BSP.vcxproj.filters
  7. 152
      PS1MDL/PS1MDL.vcxproj
  8. 33
      PS1MDL/PS1MDL.vcxproj.filters
  9. BIN
      PS1MDL/h_player.mdl
  10. 227
      PS1MDL/main.c
  11. 93
      PS1MDL/mdl.h
  12. BIN
      PS1MDL/ogre.mdl
  13. BIN
      PS1MDL/palette.lmp
  14. BIN
      PS1MDL/player.mdl
  15. 162
      PS1MDL/ps1anorms.h
  16. 48
      PS1MDL/ps1mdl.h
  17. BIN
      PS1MDL/quaddama.mdl
  18. BIN
      PS1MDL/quake.pal
  19. BIN
      PS1MDL/shambler.mdl
  20. BIN
      PS1MDL/soldier.mdl
  21. 181
      anorms.h
  22. 98
      bsp.h
  23. 180
      main.cpp
  24. 60
      ps1bsp.h
  25. 286
      rectpack/best_bin_finder.h
  26. 70
      rectpack/empty_space_allocators.h
  27. 149
      rectpack/empty_spaces.h
  28. 155
      rectpack/finders_interface.h
  29. 135
      rectpack/insert_and_split.h
  30. 78
      rectpack/rect_structs.h

12
.gitignore

@ -0,0 +1,12 @@
.vs/
Debug/
*.raw
*.pcx
*.RoQ
*.wav
*.mkv
*.avi
*.png
*.tim
*.ps1mdl
*.tga

BIN
N64E1M1.bsp

BIN
N64E2M2.bsp

41
PS1BSP.sln

@ -0,0 +1,41 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32002.261
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PS1BSP", "PS1BSP.vcxproj", "{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PS1MDL", "PS1MDL\PS1MDL.vcxproj", "{07BE9153-FB50-492F-9456-ADE90372E575}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Debug|x64.ActiveCfg = Debug|x64
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Debug|x64.Build.0 = Debug|x64
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Debug|x86.ActiveCfg = Debug|Win32
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Debug|x86.Build.0 = Debug|Win32
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Release|x64.ActiveCfg = Release|x64
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Release|x64.Build.0 = Release|x64
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Release|x86.ActiveCfg = Release|Win32
{F5AD5AE7-32D3-48FB-AAE1-261E00418C1D}.Release|x86.Build.0 = Release|Win32
{07BE9153-FB50-492F-9456-ADE90372E575}.Debug|x64.ActiveCfg = Debug|x64
{07BE9153-FB50-492F-9456-ADE90372E575}.Debug|x64.Build.0 = Debug|x64
{07BE9153-FB50-492F-9456-ADE90372E575}.Debug|x86.ActiveCfg = Debug|Win32
{07BE9153-FB50-492F-9456-ADE90372E575}.Debug|x86.Build.0 = Debug|Win32
{07BE9153-FB50-492F-9456-ADE90372E575}.Release|x64.ActiveCfg = Release|x64
{07BE9153-FB50-492F-9456-ADE90372E575}.Release|x64.Build.0 = Release|x64
{07BE9153-FB50-492F-9456-ADE90372E575}.Release|x86.ActiveCfg = Release|Win32
{07BE9153-FB50-492F-9456-ADE90372E575}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B5C5C921-F256-48A3-AA40-AEA343FE0F08}
EndGlobalSection
EndGlobal

153
PS1BSP.vcxproj

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{f5ad5ae7-32d3-48fb-aae1-261e00418c1d}</ProjectGuid>
<RootNamespace>PS1BSP</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="bsp.h" />
<ClInclude Include="ps1bsp.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

30
PS1BSP.vcxproj.filters

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="bsp.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ps1bsp.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

152
PS1MDL/PS1MDL.vcxproj

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{07be9153-fb50-492f-9456-ade90372e575}</ProjectGuid>
<RootNamespace>PS1MDL</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\anorms.h" />
<ClInclude Include="mdl.h" />
<ClInclude Include="ps1mdl.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

33
PS1MDL/PS1MDL.vcxproj.filters

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="mdl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ps1mdl.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\anorms.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

BIN
PS1MDL/h_player.mdl

227
PS1MDL/main.c

@ -0,0 +1,227 @@
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include "mdl.h"
#include "ps1mdl.h"
#define PALETTE_SIZE 256
#define NUMVERTEXNORMALS 162
static double anorms[NUMVERTEXNORMALS][3] = {
#include "../anorms.h"
};
int convertPalette(const char* lmpFile)
{
FILE* fp;
const short palette_size = PALETTE_SIZE;
unsigned char palette[PALETTE_SIZE * 3];
fopen_s(&fp, lmpFile, "rb");
if (fp == NULL)
return 0;
fread(palette, sizeof(unsigned char) * 3, PALETTE_SIZE, fp);
fclose(fp);
int file_size = PALETTE_SIZE * 4 + 24, tmp;
fopen_s(&fp, "quake.pal", "wb");
if (fp == NULL)
return 0;
fwrite("RIFF", sizeof(char), 4, fp);
tmp = file_size - 8;
fwrite(&tmp, sizeof(int), 1, fp);
fwrite("PAL data", sizeof(char), 8, fp);
tmp = file_size - 20;
fwrite(&tmp, sizeof(int), 1, fp);
fwrite("\000\003", sizeof(char), 2, fp);
fwrite(&palette_size, sizeof(short), 1, fp);
for (int i = 0; i < PALETTE_SIZE; ++i)
{
fwrite(palette + i * 3, 3, 1, fp);
fwrite("\000", sizeof(char), 1, fp);
}
tmp = 1337;
fwrite(&tmp, sizeof(int), 1, fp);
fclose(fp);
return 1;
}
static void toFixedVector(const vec3_t inVec, int outVec[3])
{
const int scale = 1 << 12;
outVec[0] = (int)(inVec[0] * scale);
outVec[1] = (int)(inVec[1] * scale);
outVec[2] = (int)(inVec[2] * scale);
}
int convertModel(const char* outFile, const mdl_header_t* inHeader, const mdl_texcoord_t* inTexCoords, const mdl_triangle_t* inTriangles, const mdl_frame_t* inFrames)
{
FILE* fout;
fopen_s(&fout, outFile, "wb");
if (fout == NULL)
return 0;
// Header
ps1mdl_header_t outHeader;
outHeader.ident = inHeader->ident;
outHeader.version = inHeader->version;
toFixedVector(inHeader->scale, outHeader.scale);
toFixedVector(inHeader->translate, outHeader.translate);
outHeader.skinWidth = (unsigned short)inHeader->skinwidth;
outHeader.skinHeight = (unsigned short)inHeader->skinheight;
outHeader.vertexCount = (unsigned short)inHeader->num_verts;
outHeader.triangleCount = (unsigned short)inHeader->num_tris;
outHeader.frameCount = (unsigned short)inHeader->num_frames;
outHeader.pad = 0;
fwrite(&outHeader, sizeof(ps1mdl_header_t), 1, fout);
// Texture coordinates
for (int i = 0; i < inHeader->num_verts; ++i)
{
ps1mdl_texcoord_t outTexCoord;
outTexCoord.onSeam = (short)(inTexCoords[i].onseam != 0);
outTexCoord.u = (short)inTexCoords[i].s;
outTexCoord.v = (short)inTexCoords[i].t;
fwrite(&outTexCoord, sizeof(ps1mdl_texcoord_t), 1, fout);
}
// Triangles
for (int i = 0; i < inHeader->num_tris; ++i)
{
ps1mdl_triangle_t outTriangle;
outTriangle.frontFace = (short)(inTriangles[i].facesfront != 0);
outTriangle.vertexIndex[0] = (unsigned short)inTriangles[i].vertex[0];
outTriangle.vertexIndex[1] = (unsigned short)inTriangles[i].vertex[1];
outTriangle.vertexIndex[2] = (unsigned short)inTriangles[i].vertex[2];
fwrite(&outTriangle, sizeof(ps1mdl_triangle_t), 1, fout);
}
// Vertices
for (int frameIdx = 0; frameIdx < inHeader->num_frames; ++frameIdx)
{
for (int i = 0; i < inHeader->num_verts; ++i)
{
mdl_vertex_t* inVert = &inFrames[frameIdx].frame.verts[i];
ps1mdl_vertex_t outVertex;
outVertex.position[0] = inVert->v[0];
outVertex.position[1] = inVert->v[1];
outVertex.position[2] = inVert->v[2];
outVertex.normalIndex = inVert->normalIndex;
fwrite(&outVertex, sizeof(ps1mdl_vertex_t), 1, fout);
}
}
fclose(fout);
return 1;
}
void exportNormals()
{
FILE* fout;
fopen_s(&fout, "ps1anorms.h", "w");
if (fout == NULL)
return;
for (int i = 0; i < NUMVERTEXNORMALS; ++i)
{
int x = (int)(anorms[i][0] * 4096);
int y = (int)(anorms[i][1] * 4096);
int z = (int)(anorms[i][2] * 4096);
fprintf(fout, "{%d, %d, %d, 0},\n", x, y, z);
}
fclose(fout);
}
int main(int argc, char** argv)
{
FILE* f;
mdl_header_t header;
char path[_MAX_PATH];
convertPalette(argv[1]);
exportNormals();
fopen_s(&f, argv[2], "rb");
if (f == NULL)
return 1;
fread(&header, sizeof(mdl_header_t), 1, f);
printf("Header model identifier: %d, version: %d\n", header.ident, header.version);
// Skins
printf("Reading %d skins of size: %dx%d\n", header.num_skins, header.skinwidth, header.skinheight);
size_t skinSize = header.skinwidth * header.skinheight * sizeof(unsigned char);
unsigned char* buf = (unsigned char*)malloc(skinSize);
if (!buf)
{
fclose(f);
return 1;
}
for (int i = 0; i < header.num_skins; ++i)
{
int group;
fread(&group, sizeof(int), 1, f);
if (group)
{
int numPics;
fread(&numPics, sizeof(int), 1, f);
fseek(f, numPics * sizeof(float) + numPics * skinSize, SEEK_CUR);
}
else
{
fread(buf, sizeof(unsigned char), skinSize, f);
FILE* fout;
sprintf_s(path, _MAX_PATH, "skin_%d_%dx%d.raw", i, header.skinwidth, header.skinheight);
fopen_s(&fout, path, "wb");
if (fout == NULL)
continue;
fwrite(buf, sizeof(unsigned char), skinSize, fout);
fclose(fout);
}
}
free(buf);
printf("Reading geometry data for %d vertices, %d triangles, %d frames\n", header.num_verts, header.num_tris, header.num_frames);
// Texcoords & triangles (vertex indices)
mdl_texcoord_t* texCoords = (mdl_texcoord_t*)malloc(sizeof(mdl_texcoord_t) * header.num_verts);
mdl_triangle_t* triangles = (mdl_triangle_t*)malloc(sizeof(mdl_triangle_t) * header.num_tris);
fread(texCoords, sizeof(mdl_texcoord_t), header.num_verts, f);
fread(triangles, sizeof(mdl_triangle_t), header.num_tris, f);
// Frames (vertex data)
mdl_frame_t* frames = (mdl_frame_t*)malloc(sizeof(mdl_frame_t) * header.num_frames);
for (int i = 0; i < header.num_frames; ++i)
{
frames[i].frame.verts = (mdl_vertex_t*)malloc(sizeof(mdl_vertex_t) * header.num_verts);
fread(&frames[i].type, sizeof(int), 1, f);
fread(&frames[i].frame.bboxmin, sizeof(mdl_vertex_t), 1, f);
fread(&frames[i].frame.bboxmax, sizeof(mdl_vertex_t), 1, f);
fread(frames[i].frame.name, sizeof(char), 16, f);
fread(frames[i].frame.verts, sizeof(mdl_vertex_t), header.num_verts, f);
}
fclose(f);
// Write simplified model file for PS1
if (!convertModel(argv[3], &header, texCoords, triangles, frames))
return 1;
return 0;
}

93
PS1MDL/mdl.h

@ -0,0 +1,93 @@
#pragma once
/* Vector */
typedef float vec3_t[3];
/* MDL header */
typedef struct
{
int ident; /* magic number: "IDPO" */
int version; /* version: 6 */
vec3_t scale; /* scale factor */
vec3_t translate; /* translation vector */
float boundingradius;
vec3_t eyeposition; /* eyes' position */
int num_skins; /* number of textures */
int skinwidth; /* texture width */
int skinheight; /* texture height */
int num_verts; /* number of vertices */
int num_tris; /* number of triangles */
int num_frames; /* number of frames */
int synctype; /* 0 = synchron, 1 = random */
int flags; /* state flag */
float size;
} mdl_header_t;
/* Skin */
typedef struct
{
int group; /* 0 = single, 1 = group */
unsigned char* data; /* texture data */
} mdl_skin_t;
/* Group of pictures */
typedef struct
{
int group; /* 1 = group */
int nb; /* number of pics */
float* time; /* time duration for each pic */
unsigned char** data; /* texture data */
} mdl_groupskin_t;
/* Texture coords */
typedef struct
{
int onseam;
int s;
int t;
} mdl_texcoord_t;
/* Triangle info */
typedef struct
{
int facesfront; /* 0 = backface, 1 = frontface */
int vertex[3]; /* vertex indices */
} mdl_triangle_t;
/* Compressed vertex */
typedef struct
{
unsigned char v[3];
unsigned char normalIndex;
} mdl_vertex_t;
/* Simple frame */
typedef struct
{
mdl_vertex_t bboxmin; /* bouding box min */
mdl_vertex_t bboxmax; /* bouding box max */
char name[16];
mdl_vertex_t* verts; /* vertex list of the frame */
} mdl_simpleframe_t;
/* Model frame */
typedef struct
{
int type; /* 0 = simple, !0 = group */
mdl_simpleframe_t frame; /* this program can't read models
composed of group frames! */
} mdl_frame_t;
/* Group of simple frames */
typedef struct
{
int type; /* !0 = group */
mdl_vertex_t min; /* min pos in all simple frames */
mdl_vertex_t max; /* max pos in all simple frames */
float* time; /* time duration for each frame */
mdl_simpleframe_t* frames; /* simple frame list */
} mdl_groupframe_t;

BIN
PS1MDL/ogre.mdl

BIN
PS1MDL/palette.lmp

BIN
PS1MDL/player.mdl

162
PS1MDL/ps1anorms.h

@ -0,0 +1,162 @@
{-2153, 0, 3484, 0},
{-1813, 978, 3539, 0},
{-1209, 0, 3913, 0},
{-1265, 2048, 3313, 0},
{-665, 1076, 3895, 0},
{0, 0, 4096, 0},
{0, 3484, 2153, 0},
{-604, 2935, 2792, 0},
{604, 2935, 2792, 0},
{0, 2153, 3484, 0},
{1265, 2048, 3313, 0},
{2153, 0, 3484, 0},
{1209, 0, 3913, 0},
{1813, 978, 3539, 0},
{665, 1076, 3895, 0},
{-2792, 604, 2935, 0},
{-3313, 1265, 2048, 0},
{-2407, 1742, 2818, 0},
{-3484, 2153, 0, 0},
{-3539, 1813, 978, 0},
{-2935, 2792, 604, 0},
{-2818, 2407, 1742, 0},
{-2048, 3313, 1265, 0},
{-978, 3539, 1813, 0},
{-1742, 2818, 2407, 0},
{-2935, 2792, -604, 0},
{-2048, 3313, -1265, 0},
{-2153, 3484, 0, 0},
{0, 3484, -2153, 0},
{-978, 3539, -1813, 0},
{0, 3913, -1209, 0},
{-1076, 3895, -665, 0},
{0, 4096, 0, 0},
{0, 3913, 1209, 0},
{-1076, 3895, 665, 0},
{978, 3539, 1813, 0},
{1076, 3895, 665, 0},
{2048, 3313, 1265, 0},
{978, 3539, -1813, 0},
{1076, 3895, -665, 0},
{2048, 3313, -1265, 0},
{3484, 2153, 0, 0},
{2935, 2792, 604, 0},
{2935, 2792, -604, 0},
{2153, 3484, 0, 0},
{1742, 2818, 2407, 0},
{3539, 1813, 978, 0},
{2818, 2407, 1742, 0},
{3313, 1265, 2048, 0},
{2792, 604, 2935, 0},
{2407, 1742, 2818, 0},
{3913, 1209, 0, 0},
{4096, 0, 0, 0},
{3895, 665, 1076, 0},
{3484, -2153, 0, 0},
{3913, -1209, 0, 0},
{3539, -1813, 978, 0},
{3895, -665, 1076, 0},
{3313, -1265, 2048, 0},
{2792, -604, 2935, 0},
{3484, 0, 2153, 0},
{3539, 1813, -978, 0},
{3313, 1265, -2048, 0},
{3895, 665, -1076, 0},
{2153, 0, -3484, 0},
{2792, 604, -2935, 0},
{2792, -604, -2935, 0},
{3484, 0, -2153, 0},
{3313, -1265, -2048, 0},
{3539, -1813, -978, 0},
{3895, -665, -1076, 0},
{604, 2935, -2792, 0},
{1265, 2048, -3313, 0},
{1742, 2818, -2407, 0},
{1813, 978, -3539, 0},
{2407, 1742, -2818, 0},
{2818, 2407, -1742, 0},
{-604, 2935, -2792, 0},
{-1265, 2048, -3313, 0},
{0, 2153, -3484, 0},
{-2153, 0, -3484, 0},
{-1813, 978, -3539, 0},
{-1209, 0, -3913, 0},
{-665, 1076, -3895, 0},
{0, 0, -4096, 0},
{1209, 0, -3913, 0},
{665, 1076, -3895, 0},
{-1813, -978, -3539, 0},
{-1265, -2048, -3313, 0},
{-665, -1076, -3895, 0},
{0, -3484, -2153, 0},
{-604, -2935, -2792, 0},
{604, -2935, -2792, 0},
{0, -2153, -3484, 0},
{1265, -2048, -3313, 0},
{1813, -978, -3539, 0},
{665, -1076, -3895, 0},
{978, -3539, -1813, 0},
{2048, -3313, -1265, 0},
{1742, -2818, -2407, 0},
{2935, -2792, -604, 0},
{2818, -2407, -1742, 0},
{2407, -1742, -2818, 0},
{0, -3913, -1209, 0},
{0, -4096, 0, 0},
{1076, -3895, -665, 0},
{0, -3484, 2153, 0},
{0, -3913, 1209, 0},
{978, -3539, 1813, 0},
{1076, -3895, 665, 0},
{2048, -3313, 1265, 0},
{2935, -2792, 604, 0},
{2153, -3484, 0, 0},
{-978, -3539, -1813, 0},
{-2048, -3313, -1265, 0},
{-1076, -3895, -665, 0},
{-3484, -2153, 0, 0},
{-2935, -2792, -604, 0},
{-2935, -2792, 604, 0},
{-2153, -3484, 0, 0},
{-2048, -3313, 1265, 0},
{-978, -3539, 1813, 0},
{-1076, -3895, 665, 0},
{-3539, -1813, 978, 0},
{-3313, -1265, 2048, 0},
{-2818, -2407, 1742, 0},
{-2792, -604, 2935, 0},
{-1813, -978, 3539, 0},
{-2407, -1742, 2818, 0},
{-1265, -2048, 3313, 0},
{-604, -2935, 2792, 0},
{-1742, -2818, 2407, 0},
{-665, -1076, 3895, 0},
{1813, -978, 3539, 0},
{665, -1076, 3895, 0},
{1265, -2048, 3313, 0},
{604, -2935, 2792, 0},
{0, -2153, 3484, 0},
{1742, -2818, 2407, 0},
{2407, -1742, 2818, 0},
{2818, -2407, 1742, 0},
{-3913, 1209, 0, 0},
{-3895, 665, 1076, 0},
{-4096, 0, 0, 0},
{-3484, 0, 2153, 0},
{-3913, -1209, 0, 0},
{-3895, -665, 1076, 0},
{-3539, 1813, -978, 0},
{-3895, 665, -1076, 0},
{-3313, 1265, -2048, 0},
{-3539, -1813, -978, 0},
{-3895, -665, -1076, 0},
{-3313, -1265, -2048, 0},
{-2792, 604, -2935, 0},
{-2792, -604, -2935, 0},
{-3484, 0, -2153, 0},
{-2818, 2407, -1742, 0},
{-2407, 1742, -2818, 0},
{-1742, 2818, -2407, 0},
{-1742, -2818, -2407, 0},
{-2407, -1742, -2818, 0},
{-2818, -2407, -1742, 0},

48
PS1MDL/ps1mdl.h

@ -0,0 +1,48 @@
#ifndef __PS1MDL_H__
#define __PS1MDL_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
int ident;
int version;
int scale[3];
int translate[3];
unsigned short skinWidth;
unsigned short skinHeight;
unsigned short vertexCount;
unsigned short triangleCount;
unsigned short frameCount;
unsigned short pad;
} ps1mdl_header_t;
typedef struct
{
short onSeam;
short u, v;
} ps1mdl_texcoord_t;
typedef struct
{
short frontFace;
unsigned short vertexIndex[3];
} ps1mdl_triangle_t;
typedef struct
{
unsigned char position[3];
unsigned char normalIndex;
} ps1mdl_vertex_t;
#ifdef __cplusplus
}
#endif
#endif // __PS1BSP_H__

BIN
PS1MDL/quaddama.mdl

BIN
PS1MDL/quake.pal

BIN
PS1MDL/shambler.mdl

BIN
PS1MDL/soldier.mdl

181
anorms.h

@ -0,0 +1,181 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
{-0.525731, 0.000000, 0.850651},
{-0.442863, 0.238856, 0.864188},
{-0.295242, 0.000000, 0.955423},
{-0.309017, 0.500000, 0.809017},
{-0.162460, 0.262866, 0.951056},
{0.000000, 0.000000, 1.000000},
{0.000000, 0.850651, 0.525731},
{-0.147621, 0.716567, 0.681718},
{0.147621, 0.716567, 0.681718},
{0.000000, 0.525731, 0.850651},
{0.309017, 0.500000, 0.809017},
{0.525731, 0.000000, 0.850651},
{0.295242, 0.000000, 0.955423},
{0.442863, 0.238856, 0.864188},
{0.162460, 0.262866, 0.951056},
{-0.681718, 0.147621, 0.716567},
{-0.809017, 0.309017, 0.500000},
{-0.587785, 0.425325, 0.688191},
{-0.850651, 0.525731, 0.000000},
{-0.864188, 0.442863, 0.238856},
{-0.716567, 0.681718, 0.147621},
{-0.688191, 0.587785, 0.425325},
{-0.500000, 0.809017, 0.309017},
{-0.238856, 0.864188, 0.442863},
{-0.425325, 0.688191, 0.587785},
{-0.716567, 0.681718, -0.147621},
{-0.500000, 0.809017, -0.309017},
{-0.525731, 0.850651, 0.000000},
{0.000000, 0.850651, -0.525731},
{-0.238856, 0.864188, -0.442863},
{0.000000, 0.955423, -0.295242},
{-0.262866, 0.951056, -0.162460},
{0.000000, 1.000000, 0.000000},
{0.000000, 0.955423, 0.295242},
{-0.262866, 0.951056, 0.162460},
{0.238856, 0.864188, 0.442863},
{0.262866, 0.951056, 0.162460},
{0.500000, 0.809017, 0.309017},
{0.238856, 0.864188, -0.442863},
{0.262866, 0.951056, -0.162460},
{0.500000, 0.809017, -0.309017},
{0.850651, 0.525731, 0.000000},
{0.716567, 0.681718, 0.147621},
{0.716567, 0.681718, -0.147621},
{0.525731, 0.850651, 0.000000},
{0.425325, 0.688191, 0.587785},
{0.864188, 0.442863, 0.238856},
{0.688191, 0.587785, 0.425325},
{0.809017, 0.309017, 0.500000},
{0.681718, 0.147621, 0.716567},
{0.587785, 0.425325, 0.688191},
{0.955423, 0.295242, 0.000000},
{1.000000, 0.000000, 0.000000},
{0.951056, 0.162460, 0.262866},
{0.850651, -0.525731, 0.000000},
{0.955423, -0.295242, 0.000000},
{0.864188, -0.442863, 0.238856},
{0.951056, -0.162460, 0.262866},
{0.809017, -0.309017, 0.500000},
{0.681718, -0.147621, 0.716567},
{0.850651, 0.000000, 0.525731},
{0.864188, 0.442863, -0.238856},
{0.809017, 0.309017, -0.500000},
{0.951056, 0.162460, -0.262866},
{0.525731, 0.000000, -0.850651},
{0.681718, 0.147621, -0.716567},
{0.681718, -0.147621, -0.716567},
{0.850651, 0.000000, -0.525731},
{0.809017, -0.309017, -0.500000},
{0.864188, -0.442863, -0.238856},
{0.951056, -0.162460, -0.262866},
{0.147621, 0.716567, -0.681718},
{0.309017, 0.500000, -0.809017},
{0.425325, 0.688191, -0.587785},
{0.442863, 0.238856, -0.864188},
{0.587785, 0.425325, -0.688191},
{0.688191, 0.587785, -0.425325},
{-0.147621, 0.716567, -0.681718},
{-0.309017, 0.500000, -0.809017},
{0.000000, 0.525731, -0.850651},
{-0.525731, 0.000000, -0.850651},
{-0.442863, 0.238856, -0.864188},
{-0.295242, 0.000000, -0.955423},
{-0.162460, 0.262866, -0.951056},
{0.000000, 0.000000, -1.000000},
{0.295242, 0.000000, -0.955423},
{0.162460, 0.262866, -0.951056},
{-0.442863, -0.238856, -0.864188},
{-0.309017, -0.500000, -0.809017},
{-0.162460, -0.262866, -0.951056},
{0.000000, -0.850651, -0.525731},
{-0.147621, -0.716567, -0.681718},
{0.147621, -0.716567, -0.681718},
{0.000000, -0.525731, -0.850651},
{0.309017, -0.500000, -0.809017},
{0.442863, -0.238856, -0.864188},
{0.162460, -0.262866, -0.951056},
{0.238856, -0.864188, -0.442863},
{0.500000, -0.809017, -0.309017},
{0.425325, -0.688191, -0.587785},
{0.716567, -0.681718, -0.147621},
{0.688191, -0.587785, -0.425325},
{0.587785, -0.425325, -0.688191},
{0.000000, -0.955423, -0.295242},
{0.000000, -1.000000, 0.000000},
{0.262866, -0.951056, -0.162460},
{0.000000, -0.850651, 0.525731},
{0.000000, -0.955423, 0.295242},
{0.238856, -0.864188, 0.442863},
{0.262866, -0.951056, 0.162460},
{0.500000, -0.809017, 0.309017},
{0.716567, -0.681718, 0.147621},
{0.525731, -0.850651, 0.000000},
{-0.238856, -0.864188, -0.442863},
{-0.500000, -0.809017, -0.309017},
{-0.262866, -0.951056, -0.162460},
{-0.850651, -0.525731, 0.000000},
{-0.716567, -0.681718, -0.147621},
{-0.716567, -0.681718, 0.147621},
{-0.525731, -0.850651, 0.000000},
{-0.500000, -0.809017, 0.309017},
{-0.238856, -0.864188, 0.442863},
{-0.262866, -0.951056, 0.162460},
{-0.864188, -0.442863, 0.238856},
{-0.809017, -0.309017, 0.500000},
{-0.688191, -0.587785, 0.425325},
{-0.681718, -0.147621, 0.716567},
{-0.442863, -0.238856, 0.864188},
{-0.587785, -0.425325, 0.688191},
{-0.309017, -0.500000, 0.809017},
{-0.147621, -0.716567, 0.681718},
{-0.425325, -0.688191, 0.587785},
{-0.162460, -0.262866, 0.951056},
{0.442863, -0.238856, 0.864188},
{0.162460, -0.262866, 0.951056},
{0.309017, -0.500000, 0.809017},
{0.147621, -0.716567, 0.681718},
{0.000000, -0.525731, 0.850651},
{0.425325, -0.688191, 0.587785},
{0.587785, -0.425325, 0.688191},
{0.688191, -0.587785, 0.425325},
{-0.955423, 0.295242, 0.000000},
{-0.951056, 0.162460, 0.262866},
{-1.000000, 0.000000, 0.000000},
{-0.850651, 0.000000, 0.525731},
{-0.955423, -0.295242, 0.000000},
{-0.951056, -0.162460, 0.262866},
{-0.864188, 0.442863, -0.238856},
{-0.951056, 0.162460, -0.262866},
{-0.809017, 0.309017, -0.500000},
{-0.864188, -0.442863, -0.238856},
{-0.951056, -0.162460, -0.262866},
{-0.809017, -0.309017, -0.500000},
{-0.681718, 0.147621, -0.716567},
{-0.681718, -0.147621, -0.716567},
{-0.850651, 0.000000, -0.525731},
{-0.688191, 0.587785, -0.425325},
{-0.587785, 0.425325, -0.688191},
{-0.425325, 0.688191, -0.587785},
{-0.425325, -0.688191, -0.587785},
{-0.587785, -0.425325, -0.688191},
{-0.688191, -0.587785, -0.425325},

98
bsp.h

@ -0,0 +1,98 @@
#pragma once
typedef struct // A Directory entry
{
long offset; // Offset to entry, in bytes, from start of file
long size; // Size of entry in file, in bytes
} dentry_t;
typedef struct // The BSP file header
{
long version; // Model version, must be 0x17 (23).
dentry_t entities; // List of Entities.
dentry_t planes; // Map Planes.
// numplanes = size/sizeof(plane_t)
dentry_t miptex; // Wall Textures.
dentry_t vertices; // Map Vertices.
// numvertices = size/sizeof(vertex_t)
dentry_t visilist; // Leaves Visibility lists.
dentry_t nodes; // BSP Nodes.
// numnodes = size/sizeof(node_t)
dentry_t texinfo; // Texture Info for faces.
// numtexinfo = size/sizeof(texinfo_t)
dentry_t faces; // Faces of each surface.
// numfaces = size/sizeof(face_t)
dentry_t lightmaps; // Wall Light Maps.
dentry_t clipnodes; // clip nodes, for Models.
// numclips = size/sizeof(clipnode_t)
dentry_t leaves; // BSP Leaves.
// numlaves = size/sizeof(leaf_t)
dentry_t lface; // List of Faces.
dentry_t edges; // Edges of faces.
// numedges = Size/sizeof(edge_t)
dentry_t ledges; // List of Edges.
dentry_t models; // List of Models.
// nummodels = Size/sizeof(model_t)
} dheader_t;
typedef float scalar_t; // Scalar value,
typedef struct // Vector or Position
{
scalar_t x; // horizontal
scalar_t y; // horizontal
scalar_t z; // vertical
} vec3_t;
typedef struct // Bounding Box, Float values
{
vec3_t min; // minimum values of X,Y,Z
vec3_t max; // maximum values of X,Y,Z
} boundbox_t;
typedef struct // Bounding Box, Short values
{
short min; // minimum values of X,Y,Z
short max; // maximum values of X,Y,Z
} bboxshort_t;
typedef struct // Mip texture list header
{
long numtex; // Number of textures in Mip Texture list
long *offset; // Offset to each of the individual texture
} mipheader_t; // from the beginning of mipheader_t
typedef struct // Mip Texture
{
char name[16]; // Name of the texture.
unsigned long width; // width of picture, must be a multiple of 8
unsigned long height; // height of picture, must be a multiple of 8
unsigned long offset1; // offset to u_char Pix[width * height]
unsigned long offset2; // offset to u_char Pix[width/2 * height/2]
unsigned long offset4; // offset to u_char Pix[width/4 * height/4]
unsigned long offset8; // offset to u_char Pix[width/8 * height/8]
} miptex_t;
typedef struct
{
float X; // X,Y,Z coordinates of the vertex
float Y; // usually some integer value
float Z; // but coded in floating point
} vertex_t;
typedef struct
{
unsigned short plane_id; // The plane in which the face lies
// must be in [0,numplanes[
unsigned short side; // 0 if in front of the plane, 1 if behind the plane
long ledge_id; // first edge in the List of edges
// must be in [0,numledges[
unsigned short ledge_num; // number of edges in the List of edges
unsigned short texinfo_id; // index of the Texture info the face is part of
// must be in [0,numtexinfos[
unsigned char typelight; // type of lighting, for the face
unsigned char baselight; // from 0xFF (dark) to 0 (bright)
unsigned char light[2]; // two additional light models
long lightmap; // Pointer inside the general light map, or -1
// this define the start of the face light map
} face_t;

180
main.cpp

@ -0,0 +1,180 @@
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include "bsp.h"
#include "rectpack/finders_interface.h"
int main(int argc, char** argv)
{
FILE* f;
dheader_t header;
char path[_MAX_PATH];
fopen_s(&f, argv[1], "rb");
if (f == NULL)
return 1;
fread(&header, sizeof(dheader_t), 1, f);
printf("Header model version: %d\n", header.version);
// Test reading the entity string data
fseek(f, header.entities.offset, SEEK_SET);
char* entities = (char*)malloc((header.entities.size + 1) * sizeof(char));
if (entities == NULL)
{
fclose(f);
return 1;
}
memset(entities, 0, (header.entities.size + 1) * sizeof(char));
fread(entities, sizeof(char), header.entities.size, f);
printf("Entities list:\n%s\n", entities);
free(entities);
// Test exporting texture data
fseek(f, header.miptex.offset, SEEK_SET);
mipheader_t mipheader;
fread(&mipheader.numtex, sizeof(long), 1, f);
mipheader.offset = (long*)malloc(mipheader.numtex * sizeof(long));
if (mipheader.offset == NULL)
{
fclose(f);
return 1;
}
fread(mipheader.offset, sizeof(long), mipheader.numtex, f);
using spaces_type = rectpack2D::empty_spaces<false>;
using rect_type = rectpack2D::output_rect_t<spaces_type>;
auto report_successful = [](rect_type&) {
return rectpack2D::callback_result::CONTINUE_PACKING;
};
auto report_unsuccessful = [](rect_type&) {
return rectpack2D::callback_result::ABORT_PACKING;
};
const auto max_side = 512; // Max height of PS1 VRAM. 8-bit textures take up half the horizontal space so this is 256x512 in practice, or a quarter of the PS1's VRAM allocation.
const auto discard_step = -4;
std::vector<rect_type> rectangles;
miptex_t miptex;
// Try some texture packing and see if we fit inside the PS1's VRAM
for (int texNum = 0; texNum < mipheader.numtex; ++texNum)
{
unsigned long miptexOffset = header.miptex.offset + mipheader.offset[texNum];
fseek(f, miptexOffset, SEEK_SET);
fread(&miptex, sizeof(miptex_t), 1, f);
//printf("Texture %d (%dx%d): %.16s\n", texNum, miptex.width, miptex.height, miptex.name);
// Shrink the larger textures, but keep smaller ones at their original size
int ps1mip = miptex.width > 64 || miptex.height > 64 ? 1 : 0;
if (strcmp(miptex.name, "clip") && strcmp(miptex.name, "trigger"))
rectangles.emplace_back(rectpack2D::rect_xywh(0, 0, miptex.width >> ps1mip, miptex.height >> ps1mip));
else
rectangles.emplace_back(rectpack2D::rect_xywh(0, 0, 0, 0));
}
// Automatic atlas packing. Nice but it tries to make a square atlas which is not what we want. (This is solved by hacking the header itself)
const auto result_size = rectpack2D::find_best_packing<spaces_type>(
rectangles,
rectpack2D::make_finder_input(
max_side,
discard_step,
report_successful,
report_unsuccessful,
rectpack2D::flipping_option::DISABLED
)
);
printf("%d textures. Packed texture atlas size: %d x %d\n", mipheader.numtex, result_size.w, result_size.h);
unsigned char* atlas = (unsigned char*)malloc(result_size.w * result_size.h * sizeof(unsigned char));
memset(atlas, 0, result_size.w * result_size.h * sizeof(unsigned char));
// Try to construct the texture atlas, see what we get
for (int texNum = 0; texNum < mipheader.numtex; ++texNum)
{
unsigned long miptexOffset = header.miptex.offset + mipheader.offset[texNum];
fseek(f, miptexOffset, SEEK_SET);
fread(&miptex, sizeof(miptex_t), 1, f);
char* outName = miptex.name;
if (*outName == '*' || *outName == '+')
outName++;
for (int mipLevel = 0; mipLevel < 4; ++mipLevel)
{
unsigned long mipOffset = *(&miptex.offset1 + mipLevel);
fseek(f, miptexOffset + mipOffset, SEEK_SET);
size_t numBytes = (miptex.width * miptex.height) >> mipLevel;
unsigned char* texBytes = (unsigned char*)malloc(sizeof(unsigned char) * numBytes);
fread(texBytes, sizeof(unsigned char), numBytes, f);
FILE* fout;
sprintf_s(path, _MAX_PATH, "textures/%s-mip%d-%dx%d.raw", outName, mipLevel, miptex.width >> mipLevel, miptex.height >> mipLevel);
fopen_s(&fout, path, "wb");
fwrite(texBytes, sizeof(unsigned char), numBytes, fout);
fclose(fout);
const auto& rectangle = rectangles[texNum];
if (miptex.width >> mipLevel == rectangle.w) // This is the mip level we've previously decided we want for our PS1 atlas
{
//printf("Writing texture %s mip %d to position: (%d, %d) w = %d, h = %d\n", miptex.name, mipLevel, rectangle.x, rectangle.y, rectangle.w, rectangle.h);
for (int y = 0; y < rectangle.h; ++y)
{
memcpy_s(atlas + ((rectangle.y + y) * result_size.w + rectangle.x), rectangle.w * sizeof(unsigned char), texBytes + (y * rectangle.w), rectangle.w * sizeof(unsigned char));
}
}
free(texBytes);
}
}
FILE* fatlas;
sprintf_s(path, _MAX_PATH, "atlas-%dx%d.raw", result_size.w, result_size.h);
fopen_s(&fatlas, path, "wb");
fwrite(atlas, sizeof(unsigned char), result_size.w * result_size.h, fatlas);
fclose(fatlas);
free(atlas);
free(mipheader.offset);
// Inspect vertex data
int numVertices = header.vertices.size / sizeof(vertex_t);
int numFaces = header.faces.size / sizeof(face_t);
fseek(f, header.vertices.offset, SEEK_SET);
vertex_t* vertices = (vertex_t*)malloc(header.vertices.size);
fread(vertices, sizeof(vertex_t), numVertices, f);
vec3_t min = { FLT_MAX, FLT_MAX, FLT_MAX }, max = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
for (int vertIdx = 0; vertIdx < numVertices; ++vertIdx)
{
vertex_t* vert = &vertices[vertIdx];
if (vert->X > max.x) max.x = vert->X;
if (vert->Y > max.y) max.y = vert->Y;
if (vert->Z > max.z) max.z = vert->Z;
if (vert->X < min.x) min.x = vert->X;
if (vert->Y < min.y) min.y = vert->Y;
if (vert->Z < min.z) min.z = vert->Z;
}
printf("%d vertices, %d faces, min = (%f, %f, %f), max = (%f, %f, %f)\n", numVertices, numFaces, min.x, min.y, min.z, max.x, max.y, max.z);
const int fixedScale = 1 << 14;
int fixedMin[3] = { (int)(min.x * fixedScale), (int)(min.y * fixedScale), (int)(min.z * fixedScale) };
int fixedMax[3] = { (int)(max.x * fixedScale), (int)(max.y * fixedScale), (int)(max.z * fixedScale) };
printf("Fixed point min = (%d, %d, %d), max = (%d, %d, %d)\n", fixedMin[0], fixedMin[1], fixedMin[2], fixedMax[0], fixedMax[1], fixedMax[2]);
fclose(f);
return 0;
}

60
ps1bsp.h

@ -0,0 +1,60 @@
#ifndef __PS1BSP_H__
#define __PS1BSP_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
unsigned char w, h; // These may be necessary for scaling UVs, especially since we use a mix of mip0 and mip1 textures
int tpage; // Texture page in PS1 VRAM (precalculated when generating the texture atlas)
short uoffs, voffs; // Texture coordinate offset within the texture page
unsigned short nextframe; // If non-zero, the texture is animated and this points to the next texture in the sequence
} ps1bsp_texture_t;
// This matches the SVECTOR data type, using the extra padding to store vertex color data.
// The full range and precision required cannot be stored in just shorts, so we make use of a floating origin stored in the BSP leafs.
// With this the higher-order bits of each vertex position are calculated into the model-view matrix, giving good precision for polygons near the camera.
typedef struct
{
short x;
short y;
short z;
unsigned char baseLight, finalLight; // Used for gouraud shading based on static lightmap data
} ps1bsp_vertex_t;
// Instead of edges as in the original BSP format, we store triangles for easy consumption by the PS1
typedef struct
{
unsigned short vertex0;
unsigned short vertex1;
unsigned short vertex2;
} ps1bsp_triangle_t;
// Pre-parsed and encoded entity data (this runs the risk of becoming too bloated)
typedef struct
{
unsigned short classtype; // Hash of the original classname
short angle[3]; // Can store both mangle (all axes) and just angle (Z-axis rotation only)
int origin[3]; // In 12-bit fixed point coordinates
unsigned int spawnflags;
unsigned short message_id; // Index into a pool of pre-defined messages
} ps1bsp_entity_t;
typedef struct
{
unsigned char length;
char message[];
} ps1bsp_message_t;
typedef struct
{
// TODO: add floating origin position, so face vertices can be moved relative to the camera position
} ps1bsp_leaf_t;
#ifdef __cplusplus
}
#endif
#endif // __PS1BSP_H__

286
rectpack/best_bin_finder.h

@ -0,0 +1,286 @@
#pragma once
#include <variant>
#include <cassert>
#include "rect_structs.h"
namespace rectpack2D {
enum class callback_result {
ABORT_PACKING,
CONTINUE_PACKING
};
template <class T>
auto& dereference(T& r) {
/*
This will allow us to pass orderings that consist of pointers,
as well as ones that are just plain objects in a vector.
*/
if constexpr(std::is_pointer_v<T>) {
return *r;
}
else {
return r;
}
};
/*
This function will do a binary search on viable bin sizes,
starting from the biggest one: starting_bin.
The search stops when the bin was successfully inserted into,
AND the bin size to be tried next differs in size from the last viable one by *less* then discard_step.
If we could not insert all input rectangles into a bin even as big as the starting_bin - the search fails.
In this case, we return the amount of space (total_area_type) inserted in total.
If we've found a viable bin that is smaller or equal to starting_bin, the search succeeds.
In this case, we return the viable bin (rect_wh).
*/
enum class bin_dimension {
BOTH,
WIDTH,
HEIGHT
};
template <class empty_spaces_type, class O>
std::variant<total_area_type, rect_wh> best_packing_for_ordering_impl(
empty_spaces_type& root,
O ordering,
const rect_wh starting_bin,
int discard_step,
const bin_dimension tried_dimension
) {
auto candidate_bin = starting_bin;
int tries_before_discarding = 0;
if (discard_step <= 0)
{
tries_before_discarding = -discard_step;
discard_step = 1;
}
//std::cout << "best_packing_for_ordering_impl dim: " << int(tried_dimension) << " w: " << starting_bin.w << " h: " << starting_bin.h << std::endl;
int starting_step = 0;
if (tried_dimension == bin_dimension::BOTH) {
candidate_bin.w /= 2;
candidate_bin.h /= 2;
starting_step = candidate_bin.w / 2;
}
else if (tried_dimension == bin_dimension::WIDTH) {
candidate_bin.w /= 2;
starting_step = candidate_bin.w / 2;
}
else {
candidate_bin.h /= 2;
starting_step = candidate_bin.h / 2;
}
for (int step = starting_step; ; step = std::max(1, step / 2)) {
//std::cout << "candidate: " << candidate_bin.w << "x" << candidate_bin.h << std::endl;
root.reset(candidate_bin);
int total_inserted_area = 0;
const bool all_inserted = [&]() {
for (const auto& r : ordering) {
const auto& rect = dereference(r);
if (root.insert(rect.get_wh())) {
total_inserted_area += rect.area();
}
else {
return false;
}
}
return true;
}();
if (all_inserted) {
/* Attempt was successful. Try with a smaller bin. */
if (step <= discard_step) {
if (tries_before_discarding > 0)
{
tries_before_discarding--;
}
else
{
return candidate_bin;
}
}
if (tried_dimension == bin_dimension::BOTH) {
candidate_bin.w -= step;
candidate_bin.h -= step;
}
else if (tried_dimension == bin_dimension::WIDTH) {
candidate_bin.w -= step;
}
else {
candidate_bin.h -= step;
}
root.reset(candidate_bin);
}
else {
/* Attempt ended with failure. Try with a bigger bin. */
if (tried_dimension == bin_dimension::BOTH) {
candidate_bin.w += step;
candidate_bin.h += step;
if (candidate_bin.area() > starting_bin.area()) {
return total_inserted_area;
}
}
else if (tried_dimension == bin_dimension::WIDTH) {
candidate_bin.w += step;
if (candidate_bin.w > starting_bin.w) {
return total_inserted_area;
}
}
else {
candidate_bin.h += step;
if (candidate_bin.h > starting_bin.h) {
return total_inserted_area;
}
}
}
}
}
template <class empty_spaces_type, class O>
std::variant<total_area_type, rect_wh> best_packing_for_ordering(
empty_spaces_type& root,
O&& ordering,
const rect_wh starting_bin,
const int discard_step
) {
const auto try_pack = [&](
const bin_dimension tried_dimension,
const rect_wh starting_bin
) {
return best_packing_for_ordering_impl(
root,
std::forward<O>(ordering),
starting_bin,
discard_step,
tried_dimension
);
};
const auto best_result = try_pack(bin_dimension::BOTH, starting_bin);
if (const auto failed = std::get_if<total_area_type>(&best_result)) {
return *failed;
}
auto best_bin = std::get<rect_wh>(best_result);
auto trial = [&](const bin_dimension tried_dimension) {
const auto trial = try_pack(tried_dimension, best_bin);
if (const auto better = std::get_if<rect_wh>(&trial)) {
best_bin = *better;
}
};
trial(bin_dimension::WIDTH);
trial(bin_dimension::HEIGHT);
return best_bin;
}
/*
This function will try to find the best bin size among the ones generated by all provided rectangle orders.
Only the best order will have results written to.
The function reports which of the rectangles did and did not fit in the end.
*/
template <
class empty_spaces_type,
class OrderType,
class F,
class I
>
rect_wh find_best_packing_impl(F for_each_order, const I input) {
const auto max_bin = rect_wh(input.max_bin_side << 1, input.max_bin_side >> 1); // PS1BSP: only way I've been able to make non-square output work ¯\_()_/¯
OrderType* best_order = nullptr;
int best_total_inserted = -1;
auto best_bin = max_bin;
/*
The root node is re-used on the TLS.
It is always reset before any packing attempt.
*/
thread_local empty_spaces_type root = rect_wh();
root.flipping_mode = input.flipping_mode;
for_each_order ([&](OrderType& current_order) {
const auto packing = best_packing_for_ordering(
root,
current_order,
max_bin,
input.discard_step
);
if (const auto total_inserted = std::get_if<total_area_type>(&packing)) {
/*
Track which function inserts the most area in total,
just in case that all orders will fail to fit into the largest allowed bin.
*/
if (best_order == nullptr) {
if (*total_inserted > best_total_inserted) {
best_order = std::addressof(current_order);
best_total_inserted = *total_inserted;
}
}
}
else if (const auto result_bin = std::get_if<rect_wh>(&packing)) {
/* Save the function if it performed the best. */
if (result_bin->area() <= best_bin.area()) {
best_order = std::addressof(current_order);
best_bin = *result_bin;
}
}
});
{
assert(best_order != nullptr);
root.reset(best_bin);
for (auto& rr : *best_order) {
auto& rect = dereference(rr);
if (const auto ret = root.insert(rect.get_wh())) {
rect = *ret;
if (callback_result::ABORT_PACKING == input.handle_successful_insertion(rect)) {
break;
}
}
else {
if (callback_result::ABORT_PACKING == input.handle_unsuccessful_insertion(rect)) {
break;
}
}
}
return root.get_rects_aabb();
}
}
}

70
rectpack/empty_space_allocators.h

@ -0,0 +1,70 @@
#pragma once
#include <array>
#include <vector>
#include <type_traits>
#include "rect_structs.h"
namespace rectpack2D {
class default_empty_spaces {
std::vector<space_rect> empty_spaces;
public:
void remove(const int i) {
empty_spaces[i] = empty_spaces.back();
empty_spaces.pop_back();
}
bool add(const space_rect r) {
empty_spaces.emplace_back(r);
return true;
}
auto get_count() const {
return empty_spaces.size();
}
void reset() {
empty_spaces.clear();
}
const auto& get(const int i) {
return empty_spaces[i];
}
};
template <int MAX_SPACES>
class static_empty_spaces {
int count_spaces = 0;
std::array<space_rect, MAX_SPACES> empty_spaces;
public:
void remove(const int i) {
empty_spaces[i] = empty_spaces[count_spaces - 1];
--count_spaces;
}
bool add(const space_rect r) {
if (count_spaces < static_cast<int>(empty_spaces.size())) {
empty_spaces[count_spaces] = r;
++count_spaces;
return true;
}
return false;
}
auto get_count() const {
return count_spaces;
}
void reset() {
count_spaces = 0;
}
const auto& get(const int i) {
return empty_spaces[i];
}
};
}

149
rectpack/empty_spaces.h

@ -0,0 +1,149 @@
#pragma once
#include "insert_and_split.h"
namespace rectpack2D {
enum class flipping_option {
DISABLED,
ENABLED
};
class default_empty_spaces;
template <bool allow_flip, class empty_spaces_provider = default_empty_spaces>
class empty_spaces {
rect_wh current_aabb;
empty_spaces_provider spaces;
/* MSVC fix for non-conformant if constexpr implementation */
static auto make_output_rect(const int x, const int y, const int w, const int h) {
return rect_xywh(x, y, w, h);
}
static auto make_output_rect(const int x, const int y, const int w, const int h, const bool flipped) {
return rect_xywhf(x, y, w, h, flipped);
}
public:
using output_rect_type = std::conditional_t<allow_flip, rect_xywhf, rect_xywh>;
flipping_option flipping_mode = flipping_option::ENABLED;
empty_spaces(const rect_wh& r) {
reset(r);
}
void reset(const rect_wh& r) {
current_aabb = {};
spaces.reset();
spaces.add(rect_xywh(0, 0, r.w, r.h));
}
template <class F>
std::optional<output_rect_type> insert(const rect_wh image_rectangle, F report_candidate_empty_space) {
for (int i = static_cast<int>(spaces.get_count()) - 1; i >= 0; --i) {
const auto candidate_space = spaces.get(i);
report_candidate_empty_space(candidate_space);
auto accept_result = [this, i, image_rectangle, candidate_space](
const created_splits& splits,
const bool flipping_necessary
) -> std::optional<output_rect_type> {
spaces.remove(i);
for (int s = 0; s < splits.count; ++s) {
if (!spaces.add(splits.spaces[s])) {
return std::nullopt;
}
}
if constexpr(allow_flip) {
const auto result = make_output_rect(
candidate_space.x,
candidate_space.y,
image_rectangle.w,
image_rectangle.h,
flipping_necessary
);
current_aabb.expand_with(result);
return result;
}
else if constexpr(!allow_flip) {
(void)flipping_necessary;
const auto result = make_output_rect(
candidate_space.x,
candidate_space.y,
image_rectangle.w,
image_rectangle.h
);
current_aabb.expand_with(result);
return result;
}
};
auto try_to_insert = [&](const rect_wh& img) {
return rectpack2D::insert_and_split(img, candidate_space);
};
if constexpr(!allow_flip) {
if (const auto normal = try_to_insert(image_rectangle)) {
return accept_result(normal, false);
}
}
else {
if (flipping_mode == flipping_option::ENABLED) {
const auto normal = try_to_insert(image_rectangle);
const auto flipped = try_to_insert(rect_wh(image_rectangle).flip());
/*
If both were successful,
prefer the one that generated less remainder spaces.
*/
if (normal && flipped) {
if (flipped.better_than(normal)) {
/* Accept the flipped result if it producues less or "better" spaces. */
return accept_result(flipped, true);
}
return accept_result(normal, false);
}
if (normal) {
return accept_result(normal, false);
}
if (flipped) {
return accept_result(flipped, true);
}
}
else {
if (const auto normal = try_to_insert(image_rectangle)) {
return accept_result(normal, false);
}
}
}
}
return std::nullopt;
}
decltype(auto) insert(const rect_wh& image_rectangle) {
return insert(image_rectangle, [](auto&){ });
}
auto get_rects_aabb() const {
return current_aabb;
}
const auto& get_spaces() const {
return spaces;
}
};
}

155
rectpack/finders_interface.h

@ -0,0 +1,155 @@
#pragma once
#include <optional>
#include <vector>
#include <array>
#include <variant>
#include <algorithm>
#include "insert_and_split.h"
#include "empty_spaces.h"
#include "empty_space_allocators.h"
#include "best_bin_finder.h"
namespace rectpack2D {
template <class empty_spaces_type>
using output_rect_t = typename empty_spaces_type::output_rect_type;
template <class F, class G>
struct finder_input {
const int max_bin_side;
const int discard_step;
F handle_successful_insertion;
G handle_unsuccessful_insertion;
const flipping_option flipping_mode;
};
template <class F, class G>
auto make_finder_input(
const int max_bin_side,
const int discard_step,
F&& handle_successful_insertion,
G&& handle_unsuccessful_insertion,
const flipping_option flipping_mode
) {
return finder_input<F, G> {
max_bin_side,
discard_step,
std::forward<F>(handle_successful_insertion),
std::forward<G>(handle_unsuccessful_insertion),
flipping_mode
};
};
/*
Finds the best packing for the rectangles,
just in the order that they were passed.
*/
template <class empty_spaces_type, class F, class G>
rect_wh find_best_packing_dont_sort(
std::vector<output_rect_t<empty_spaces_type>>& subjects,
const finder_input<F, G>& input
) {
using order_type = std::remove_reference_t<decltype(subjects)>;
return find_best_packing_impl<empty_spaces_type, order_type>(
[&subjects](auto callback) { callback(subjects); },
input
);
}
/*
Finds the best packing for the rectangles.
Accepts a list of predicates able to compare two input rectangles.
The function will try to pack the rectangles in all orders generated by the predicates,
and will only write the x, y coordinates of the best packing found among the orders.
*/
template <class empty_spaces_type, class F, class G, class Comparator, class... Comparators>
rect_wh find_best_packing(
std::vector<output_rect_t<empty_spaces_type>>& subjects,
const finder_input<F, G>& input,
Comparator comparator,
Comparators... comparators
) {
using rect_type = output_rect_t<empty_spaces_type>;
using order_type = std::vector<rect_type*>;
constexpr auto count_orders = 1 + sizeof...(Comparators);
thread_local std::array<order_type, count_orders> orders;
{
/* order[0] will always exist since this overload requires at least one comparator */
auto& initial_pointers = orders[0];
initial_pointers.clear();
for (auto& s : subjects) {
if (s.area() > 0) {
initial_pointers.emplace_back(std::addressof(s));
}
}
for (std::size_t i = 1; i < count_orders; ++i) {
orders[i] = initial_pointers;
}
}
std::size_t f = 0;
auto& orders_ref = orders;
auto make_order = [&f, &orders_ref](auto& predicate) {
std::sort(orders_ref[f].begin(), orders_ref[f].end(), predicate);
++f;
};
make_order(comparator);
(make_order(comparators), ...);
return find_best_packing_impl<empty_spaces_type, order_type>(
[&orders_ref](auto callback){ for (auto& o : orders_ref) { callback(o); } },
input
);
}
/*
Finds the best packing for the rectangles.
Provides a list of several sensible comparison predicates.
*/
template <class empty_spaces_type, class F, class G>
rect_wh find_best_packing(
std::vector<output_rect_t<empty_spaces_type>>& subjects,
const finder_input<F, G>& input
) {
using rect_type = output_rect_t<empty_spaces_type>;
return find_best_packing<empty_spaces_type>(
subjects,
input,
[](const rect_type* const a, const rect_type* const b) {
return a->area() > b->area();
},
[](const rect_type* const a, const rect_type* const b) {
return a->perimeter() > b->perimeter();
},
[](const rect_type* const a, const rect_type* const b) {
return std::max(a->w, a->h) > std::max(b->w, b->h);
},
[](const rect_type* const a, const rect_type* const b) {
return a->w > b->w;
},
[](const rect_type* const a, const rect_type* const b) {
return a->h > b->h;
},
[](const rect_type* const a, const rect_type* const b) {
return a->get_wh().pathological_mult() > b->get_wh().pathological_mult();
}
);
}
}

135
rectpack/insert_and_split.h

@ -0,0 +1,135 @@
#pragma once
#include <array>
#include "rect_structs.h"
namespace rectpack2D {
struct created_splits {
int count = 0;
std::array<space_rect, 2> spaces;
static auto failed() {
created_splits result;
result.count = -1;
return result;
}
static auto none() {
return created_splits();
}
template <class... Args>
created_splits(Args&&... args) : spaces({ std::forward<Args>(args)... }) {
count = sizeof...(Args);
}
bool better_than(const created_splits& b) const {
return count < b.count;
}
explicit operator bool() const {
return count != -1;
}
};
inline created_splits insert_and_split(
const rect_wh& im, /* Image rectangle */
const space_rect& sp /* Space rectangle */
) {
const auto free_w = sp.w - im.w;
const auto free_h = sp.h - im.h;
if (free_w < 0 || free_h < 0) {
/*
Image is bigger than the candidate empty space.
We'll need to look further.
*/
return created_splits::failed();
}
if (free_w == 0 && free_h == 0) {
/*
If the image dimensions equal the dimensions of the candidate empty space (image fits exactly),
we will just delete the space and create no splits.
*/
return created_splits::none();
}
/*
If the image fits into the candidate empty space,
but exactly one of the image dimensions equals the respective dimension of the candidate empty space
(e.g. image = 20x40, candidate space = 30x40)
we delete the space and create a single split. In this case a 10x40 space.
*/
if (free_w > 0 && free_h == 0) {
auto r = sp;
r.x += im.w;
r.w -= im.w;
return created_splits(r);
}
if (free_w == 0 && free_h > 0) {
auto r = sp;
r.y += im.h;
r.h -= im.h;
return created_splits(r);
}
/*
Every other option has been exhausted,
so at this point the image must be *strictly* smaller than the empty space,
that is, it is smaller in both width and height.
Thus, free_w and free_h must be positive.
*/
/*
Decide which way to split.
Instead of having two normally-sized spaces,
it is better - though I have no proof of that - to have a one tiny space and a one huge space.
This creates better opportunity for insertion of future rectangles.
This is why, if we had more of width remaining than we had of height,
we split along the vertical axis,
and if we had more of height remaining than we had of width,
we split along the horizontal axis.
*/
if (free_w > free_h) {
const auto bigger_split = space_rect(
sp.x + im.w,
sp.y,
free_w,
sp.h
);
const auto lesser_split = space_rect(
sp.x,
sp.y + im.h,
im.w,
free_h
);
return created_splits(bigger_split, lesser_split);
}
const auto bigger_split = space_rect(
sp.x,
sp.y + im.h,
sp.w,
free_h
);
const auto lesser_split = space_rect(
sp.x + im.w,
sp.y,
free_w,
im.h
);
return created_splits(bigger_split, lesser_split);
}
}

78
rectpack/rect_structs.h

@ -0,0 +1,78 @@
#pragma once
#include <utility>
namespace rectpack2D {
using total_area_type = int;
struct rect_wh {
rect_wh() : w(0), h(0) {}
rect_wh(const int w, const int h) : w(w), h(h) {}
int w;
int h;
auto& flip() {
std::swap(w, h);
return *this;
}
int max_side() const {
return h > w ? h : w;
}
int min_side() const {
return h < w ? h : w;
}
int area() const { return w * h; }
int perimeter() const { return 2 * w + 2 * h; }
float pathological_mult() const {
return float(max_side()) / min_side() * area();
}
template <class R>
void expand_with(const R& r) {
w = std::max(w, r.x + r.w);
h = std::max(h, r.y + r.h);
}
};
struct rect_xywh {
int x;
int y;
int w;
int h;
rect_xywh() : x(0), y(0), w(0), h(0) {}
rect_xywh(const int x, const int y, const int w, const int h) : x(x), y(y), w(w), h(h) {}
int area() const { return w * h; }
int perimeter() const { return 2 * w + 2 * h; }
auto get_wh() const {
return rect_wh(w, h);
}
};
struct rect_xywhf {
int x;
int y;
int w;
int h;
bool flipped;
rect_xywhf() : x(0), y(0), w(0), h(0), flipped(false) {}
rect_xywhf(const int x, const int y, const int w, const int h, const bool flipped) : x(x), y(y), w(flipped ? h : w), h(flipped ? w : h), flipped(flipped) {}
rect_xywhf(const rect_xywh& b) : rect_xywhf(b.x, b.y, b.w, b.h, false) {}
int area() const { return w * h; }
int perimeter() const { return 2 * w + 2 * h; }
auto get_wh() const {
return rect_wh(w, h);
}
};
using space_rect = rect_xywh;
}
Loading…
Cancel
Save