commit
8f82f467a0
30 changed files with 2343 additions and 0 deletions
-
12.gitignore
-
BINN64E1M1.bsp
-
BINN64E2M2.bsp
-
41PS1BSP.sln
-
153PS1BSP.vcxproj
-
30PS1BSP.vcxproj.filters
-
152PS1MDL/PS1MDL.vcxproj
-
33PS1MDL/PS1MDL.vcxproj.filters
-
BINPS1MDL/h_player.mdl
-
227PS1MDL/main.c
-
93PS1MDL/mdl.h
-
BINPS1MDL/ogre.mdl
-
BINPS1MDL/palette.lmp
-
BINPS1MDL/player.mdl
-
162PS1MDL/ps1anorms.h
-
48PS1MDL/ps1mdl.h
-
BINPS1MDL/quaddama.mdl
-
BINPS1MDL/quake.pal
-
BINPS1MDL/shambler.mdl
-
BINPS1MDL/soldier.mdl
-
181anorms.h
-
98bsp.h
-
180main.cpp
-
60ps1bsp.h
-
286rectpack/best_bin_finder.h
-
70rectpack/empty_space_allocators.h
-
149rectpack/empty_spaces.h
-
155rectpack/finders_interface.h
-
135rectpack/insert_and_split.h
-
78rectpack/rect_structs.h
@ -0,0 +1,12 @@ |
|||
.vs/ |
|||
Debug/ |
|||
*.raw |
|||
*.pcx |
|||
*.RoQ |
|||
*.wav |
|||
*.mkv |
|||
*.avi |
|||
*.png |
|||
*.tim |
|||
*.ps1mdl |
|||
*.tga |
|||
@ -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 |
|||
@ -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> |
|||
@ -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> |
|||
@ -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> |
|||
@ -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> |
|||
@ -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; |
|||
} |
|||
@ -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; |
|||
@ -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}, |
|||
@ -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__ |
|||
@ -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}, |
|||
@ -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; |
|||
@ -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; |
|||
} |
|||
@ -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__ |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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]; |
|||
} |
|||
}; |
|||
} |
|||
@ -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; |
|||
} |
|||
}; |
|||
} |
|||
@ -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(); |
|||
} |
|||
); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue