diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/utils | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/utils')
216 files changed, 82164 insertions, 0 deletions
diff --git a/mp/src/utils/captioncompiler/captioncompiler-2010.vcxproj b/mp/src/utils/captioncompiler/captioncompiler-2010.vcxproj new file mode 100644 index 00000000..47767523 --- /dev/null +++ b/mp/src/utils/captioncompiler/captioncompiler-2010.vcxproj @@ -0,0 +1,261 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Captioncompiler</ProjectName>
+ <ProjectGuid>{E85D01E5-DA1B-00A2-5D72-A9B6DEA9A995}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>captioncompiler</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>captioncompiler</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 captioncompiler.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\..\game\shared;.\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;captioncompiler;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\captioncompiler;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\captioncompiler.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/captioncompiler.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 captioncompiler.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\..\game\shared;.\</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;captioncompiler;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\captioncompiler;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\captioncompiler.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/captioncompiler.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\appframework.lib" />
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\tier3.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="cbase.h" />
+ <ClInclude Include="..\common\filesystem_tools.h" />
+ <ClInclude Include="..\common\cmdlib.h" />
+ <ClInclude Include="..\..\public\filesystem_helpers.h" />
+ <ClInclude Include="..\..\public\filesystem_init.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ <ClInclude Include="..\common\pacifier.h" />
+ <ClInclude Include="..\common\scriplib.h" />
+ <ClInclude Include="..\..\public\stringregistry.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\cmdlib.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="..\..\public\filesystem_init.cpp" />
+ <ClCompile Include="..\common\pacifier.cpp" />
+ <ClCompile Include="..\common\scriplib.cpp" />
+ <ClCompile Include="..\..\public\stringregistry.cpp" />
+ <ClCompile Include="captioncompiler.cpp" />
+ <ClCompile Include="..\..\common\compiledcaptionswap.cpp" />
+ <ClCompile Include="..\common\filesystem_tools.cpp" />
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/captioncompiler/captioncompiler-2010.vcxproj.filters b/mp/src/utils/captioncompiler/captioncompiler-2010.vcxproj.filters new file mode 100644 index 00000000..3e16f2b4 --- /dev/null +++ b/mp/src/utils/captioncompiler/captioncompiler-2010.vcxproj.filters @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Shared Code">
+ <UniqueIdentifier>{C0A65B3F-3B05-094C-44FA-B1624BAD7BAC}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\appframework.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier3.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="cbase.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\filesystem_tools.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\cmdlib.h">
+ <Filter>Shared Code</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem_helpers.h">
+ <Filter>Shared Code</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem_init.h">
+ <Filter>Shared Code</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Shared Code</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\pacifier.h">
+ <Filter>Shared Code</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\scriplib.h">
+ <Filter>Shared Code</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\stringregistry.h">
+ <Filter>Shared Code</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\cmdlib.cpp">
+ <Filter>Shared Code</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Shared Code</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_init.cpp">
+ <Filter>Shared Code</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\pacifier.cpp">
+ <Filter>Shared Code</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\scriplib.cpp">
+ <Filter>Shared Code</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\stringregistry.cpp">
+ <Filter>Shared Code</Filter>
+ </ClCompile>
+ <ClCompile Include="captioncompiler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\common\compiledcaptionswap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\filesystem_tools.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/captioncompiler/captioncompiler.cpp b/mp/src/utils/captioncompiler/captioncompiler.cpp new file mode 100644 index 00000000..44e1578f --- /dev/null +++ b/mp/src/utils/captioncompiler/captioncompiler.cpp @@ -0,0 +1,588 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: vcd_sound_check.cpp : Defines the entry point for the console application.
+//
+//===========================================================================//
+#include <stdio.h>
+#include <windows.h>
+#include "tier0/dbg.h"
+#include "tier1/utldict.h"
+#include "filesystem.h"
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "vstdlib/random.h"
+#include "tier1/UtlBuffer.h"
+#include "pacifier.h"
+#include "appframework/tier3app.h"
+#include "tier0/icommandline.h"
+#include "vgui/IVGui.h"
+#include "vgui_controls/controls.h"
+#include "vgui/ILocalize.h"
+#include "tier1/checksum_crc.h"
+#include "tier1/UtlSortVector.h"
+#include "tier1/utlmap.h"
+#include "captioncompiler.h"
+
+#include "tier0/fasttimer.h"
+
+using namespace vgui;
+
+// #define TESTING 1
+
+
+bool uselogfile = false;
+bool bX360 = false;
+
+struct AnalysisData
+{
+ CUtlSymbolTable symbols;
+};
+
+static AnalysisData g_Analysis;
+
+IBaseFileSystem *filesystem = NULL;
+
+static bool spewed = false;
+
+SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
+{
+ spewed = true;
+
+ printf( "%s", pMsg );
+ OutputDebugString( pMsg );
+
+ if ( type == SPEW_ERROR )
+ {
+ printf( "\n" );
+ OutputDebugString( "\n" );
+ }
+
+ return SPEW_CONTINUE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : depth -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void vprint( int depth, const char *fmt, ... )
+{
+ char string[ 8192 ];
+ va_list va;
+ va_start( va, fmt );
+ vsprintf( string, fmt, va );
+ va_end( va );
+
+ FILE *fp = NULL;
+
+ if ( uselogfile )
+ {
+ fp = fopen( "log.txt", "ab" );
+ }
+
+ while ( depth-- > 0 )
+ {
+ printf( " " );
+ OutputDebugString( " " );
+ if ( fp )
+ {
+ fprintf( fp, " " );
+ }
+ }
+
+ ::printf( string );
+ OutputDebugString( string );
+
+ if ( fp )
+ {
+ char *p = string;
+ while ( *p )
+ {
+ if ( *p == '\n' )
+ {
+ fputc( '\r', fp );
+ }
+ fputc( *p, fp );
+ p++;
+ }
+ fclose( fp );
+ }
+}
+
+void logprint( char const *logfile, const char *fmt, ... )
+{
+ char string[ 8192 ];
+ va_list va;
+ va_start( va, fmt );
+ vsprintf( string, fmt, va );
+ va_end( va );
+
+ FILE *fp = NULL;
+ static bool first = true;
+ if ( first )
+ {
+ first = false;
+ fp = fopen( logfile, "wb" );
+ }
+ else
+ {
+ fp = fopen( logfile, "ab" );
+ }
+ if ( fp )
+ {
+ char *p = string;
+ while ( *p )
+ {
+ if ( *p == '\n' )
+ {
+ fputc( '\r', fp );
+ }
+ fputc( *p, fp );
+ p++;
+ }
+ fclose( fp );
+ }
+}
+
+
+void Con_Printf( const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ vprint( 0, output );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void printusage( void )
+{
+ vprint( 0, "usage: captioncompiler closecaptionfile.txt\n\
+ \t-v = verbose output\n\
+ \t-l = log to file log.txt\n\
+ \ne.g.: kvc -l u:/xbox/game/hl2x/resource/closecaption_english.txt" );
+
+ // Exit app
+ exit( 1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CheckLogFile( void )
+{
+ if ( uselogfile )
+ {
+ _unlink( "log.txt" );
+ vprint( 0, " Outputting to log.txt\n" );
+ }
+}
+
+void PrintHeader()
+{
+ vprint( 0, "Valve Software - captioncompiler.exe (%s)\n", __DATE__ );
+ vprint( 0, "--- Close Caption File compiler ---\n" );
+}
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CCompileCaptionsApp : public CTier3SteamApp
+{
+ typedef CTier3SteamApp BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit();
+ virtual int Main();
+ virtual void PostShutdown();
+ virtual void Destroy();
+
+private:
+ // Sets up the search paths
+ bool SetupSearchPaths();
+
+ void CompileCaptionFile( char const *infile, char const *outfile );
+ void DescribeCaptions( char const *file );
+};
+
+
+bool CCompileCaptionsApp::Create()
+{
+ SpewOutputFunc( SpewFunc );
+ SpewActivate( "kvc", 2 );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ return AddSystems( appSystems );
+}
+
+void CCompileCaptionsApp::Destroy()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CCompileCaptionsApp::SetupSearchPaths()
+{
+ if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ // Set gamedir.
+ Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), GetGameInfoPath() );
+ Q_AppendSlash( gamedir, sizeof( gamedir ) );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Init, shutdown
+//-----------------------------------------------------------------------------
+bool CCompileCaptionsApp::PreInit( )
+{
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ g_pFileSystem = g_pFullFileSystem;
+ if ( !g_pFileSystem || !g_pVGui || !g_pVGuiLocalize )
+ {
+ Error( "Unable to load required library interface!\n" );
+ return false;
+ }
+
+// MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+ g_pFullFileSystem->SetWarningFunc( Warning );
+
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ return true;
+}
+
+void CCompileCaptionsApp::PostShutdown()
+{
+ g_pFileSystem = NULL;
+ BaseClass::PostShutdown();
+}
+
+void CCompileCaptionsApp::CompileCaptionFile( char const *infile, char const *outfile )
+{
+ StringIndex_t maxindex = (StringIndex_t)-1;
+ int maxunicodesize = 0;
+ int totalsize = 0;
+
+ int c = 0;
+
+ int curblock = 0;
+ int usedBytes = 0;
+ int blockSize = MAX_BLOCK_SIZE;
+
+ int freeSpace = 0;
+
+ CUtlVector< CaptionLookup_t > directory;
+ CUtlBuffer data;
+
+ CUtlRBTree< unsigned int > hashcollision( 0, 0, DefLessFunc( unsigned int ) );
+
+ for ( StringIndex_t i = g_pVGuiLocalize->GetFirstStringIndex(); i != INVALID_LOCALIZE_STRING_INDEX; i = g_pVGuiLocalize->GetNextStringIndex( i ), ++c )
+ {
+ char const *entryName = g_pVGuiLocalize->GetNameByIndex( i );
+ CaptionLookup_t entry;
+ entry.SetHash( entryName );
+
+ // vprint( 0, "%d / %d: %s == %u\n", c, i, g_pVGuiLocalize->GetNameByIndex( i ), entry.hash );
+
+ if ( hashcollision.Find( entry.hash ) != hashcollision.InvalidIndex() )
+ {
+ Error( "Hash name collision on %s!!!\n", g_pVGuiLocalize->GetNameByIndex( i ) );
+ }
+
+ hashcollision.Insert( entry.hash );
+
+ const wchar_t *text = g_pVGuiLocalize->GetValueByIndex( i );
+ if ( verbose )
+ {
+ vprint( 0, "Processing: '%30.30s' = '%S'\n", entryName, text );
+ }
+ int len = text ? ( wcslen( text ) + 1 ) * sizeof( short ) : 0;
+ if ( len > maxunicodesize )
+ {
+ maxindex = i;
+ maxunicodesize = len;
+ }
+
+ if ( len > blockSize )
+ {
+ Error( "Caption text file '%s' contains a single caption '%s' of %d bytes (%d is max), change MAX_BLOCK_SIZE in captioncompiler.h to fix!!!\n", g_pVGuiLocalize->GetNameByIndex( i ),
+ entryName, len, blockSize );
+ }
+ totalsize += len;
+
+ if ( usedBytes + len >= blockSize )
+ {
+ ++curblock;
+
+ int leftover = ( blockSize - usedBytes );
+
+ totalsize += leftover;
+
+ freeSpace += leftover;
+
+ while ( --leftover >= 0 )
+ {
+ data.PutChar( 0 );
+ }
+
+ usedBytes = len;
+ entry.offset = 0;
+
+ data.Put( (const void *)text, len );
+ }
+ else
+ {
+ entry.offset = usedBytes;
+ usedBytes += len;
+ data.Put( (const void *)text, len );
+ }
+
+ entry.length = len;
+ entry.blockNum = curblock;
+
+ directory.AddToTail( entry );
+ }
+
+ int leftover = ( blockSize - usedBytes );
+ totalsize += leftover;
+ freeSpace += leftover;
+ while ( --leftover >= 0 )
+ {
+ data.PutChar( 0 );
+ }
+
+ vprint( 0, "Found %i strings in '%s'\n", c, infile );
+
+ if ( maxindex != INVALID_LOCALIZE_STRING_INDEX )
+ {
+ vprint( 0, "Longest string '%s' = (%i) bytes average(%.3f)\n%",
+ g_pVGuiLocalize->GetNameByIndex( maxindex ), maxunicodesize, (float)totalsize/(float)c );
+ }
+
+ vprint( 0, "%d blocks (%d bytes each), %d bytes wasted (%.3f per block average), total bytes %d\n",
+ curblock + 1, blockSize, freeSpace, (float)freeSpace/(float)( curblock + 1 ), totalsize );
+
+ vprint( 0, "directory size %d entries, %d bytes, data size %d bytes\n",
+ directory.Count(), directory.Count() * sizeof( CaptionLookup_t ), data.TellPut() );
+
+ CompiledCaptionHeader_t header;
+ header.magic = COMPILED_CAPTION_FILEID;
+ header.version = COMPILED_CAPTION_VERSION;
+ header.numblocks = curblock + 1;
+ header.blocksize = blockSize;
+ header.directorysize = directory.Count();
+ header.dataoffset = 0;
+
+ // Now write the outfile
+ CUtlBuffer out;
+ out.Put( &header, sizeof( header ) );
+ out.Put( directory.Base(), directory.Count() * sizeof( CaptionLookup_t ) );
+ int curOffset = out.TellPut();
+ // Round it up to the next 512 byte boundary
+ int nBytesDestBuffer = AlignValue( curOffset, 512 ); // align to HD sector
+ int nPadding = nBytesDestBuffer - curOffset;
+ while ( --nPadding >= 0 )
+ {
+ out.PutChar( 0 );
+ }
+ out.Put( data.Base(), data.TellPut() );
+
+ // Write out a corrected header
+ header.dataoffset = nBytesDestBuffer;
+ int savePos = out.TellPut();
+ out.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
+ out.Put( &header, sizeof( header ) );
+ out.SeekPut( CUtlBuffer::SEEK_HEAD, savePos );
+
+ g_pFullFileSystem->WriteFile( outfile, NULL, out );
+
+ // Jeep: this function no longer exisits
+ /*if ( bX360 )
+ {
+ UpdateOrCreateCaptionFile_X360( g_pFullFileSystem, outfile, NULL, true );
+ }*/
+}
+
+void CCompileCaptionsApp::DescribeCaptions( char const *file )
+{
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( file, NULL, buf ) )
+ {
+ Error( "Unable to read '%s' into buffer\n", file );
+ }
+
+ CompiledCaptionHeader_t header;
+ buf.Get( &header, sizeof( header ) );
+ if ( header.magic != COMPILED_CAPTION_FILEID )
+ Error( "Invalid file id for %s\n", file );
+ if ( header.version != COMPILED_CAPTION_VERSION )
+ Error( "Invalid file version for %s\n", file );
+
+ // Read the directory
+ CUtlSortVector< CaptionLookup_t, CCaptionLookupLess > directory;
+ directory.EnsureCapacity( header.directorysize );
+ directory.CopyArray( (const CaptionLookup_t *)buf.PeekGet(), header.directorysize );
+ directory.RedoSort( true );
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, header.dataoffset );
+
+ int i;
+ CUtlVector< CaptionBlock_t > blocks;
+ for ( i = 0; i < header.numblocks; ++i )
+ {
+ CaptionBlock_t& newBlock = blocks[ blocks.AddToTail() ];
+ Q_memset( newBlock.data, 0, sizeof( newBlock.data ) );
+ buf.Get( newBlock.data, header.blocksize );
+ }
+
+ CUtlMap< unsigned int, StringIndex_t > inverseMap( 0, 0, DefLessFunc( unsigned int ) );
+ for ( StringIndex_t idx = g_pVGuiLocalize->GetFirstStringIndex(); idx != INVALID_LOCALIZE_STRING_INDEX; idx = g_pVGuiLocalize->GetNextStringIndex( idx ) )
+ {
+ const char *name = g_pVGuiLocalize->GetNameByIndex( idx );
+ CaptionLookup_t dummy;
+ dummy.SetHash( name );
+
+ inverseMap.Insert( dummy.hash, idx );
+ }
+
+ // Now print everything out...
+ for ( i = 0; i < header.directorysize; ++i )
+ {
+ const CaptionLookup_t& entry = directory[ i ];
+ char const *name = g_pVGuiLocalize->GetNameByIndex( inverseMap.Element( inverseMap.Find( entry.hash ) ) );
+ const CaptionBlock_t& block = blocks[ entry.blockNum ];
+ const wchar_t *data = (const wchar_t *)&block.data[ entry.offset ];
+ wchar_t *temp = ( wchar_t * )_alloca( entry.length * sizeof( short ) );
+ wcsncpy( temp, data, ( entry.length / sizeof( short ) ) - 1 );
+
+ vprint( 0, "%3.3d: (%40.40s) hash(%15.15u), block(%4.4d), offset(%4.4d), len(%4.4d) %S\n",
+ i, name, entry.hash, entry.blockNum, entry.offset, entry.length, temp );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CCompileCaptionsApp::Main()
+{
+ CUtlVector< CUtlSymbol > worklist;
+
+ int i = 1;
+ for ( i ; i<CommandLine()->ParmCount() ; i++)
+ {
+ if ( CommandLine()->GetParm( i )[ 0 ] == '-' )
+ {
+ switch( CommandLine()->GetParm( i )[ 1 ] )
+ {
+ case 'l':
+ uselogfile = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'x':
+ bX360 = true;
+ break;
+ case 'g': // -game
+ ++i;
+ break;
+ default:
+ printusage();
+ break;
+ }
+ }
+ else if ( i != 0 )
+ {
+ char fn[ 512 ];
+ Q_strncpy( fn, CommandLine()->GetParm( i ), sizeof( fn ) );
+ Q_FixSlashes( fn );
+ Q_strlower( fn );
+
+ CUtlSymbol sym;
+ sym = fn;
+ worklist.AddToTail( sym );
+ }
+ }
+
+ if ( CommandLine()->ParmCount() < 2 || ( i != CommandLine()->ParmCount() ) || worklist.Count() != 1 )
+ {
+ PrintHeader();
+ printusage();
+ }
+
+ CheckLogFile();
+
+ PrintHeader();
+
+ char binaries[MAX_PATH];
+ Q_strncpy( binaries, gamedir, MAX_PATH );
+ Q_StripTrailingSlash( binaries );
+ Q_strncat( binaries, "/../bin", MAX_PATH, MAX_PATH );
+
+ char outfile[ 512 ];
+ if ( Q_stristr( worklist[ worklist.Count() - 1 ].String(), gamedir ) )
+ {
+ Q_strncpy( outfile, &worklist[ worklist.Count() - 1 ].String()[ Q_strlen( gamedir ) ] , sizeof( outfile ) );
+ }
+ else
+ {
+ Q_snprintf( outfile, sizeof( outfile ), "resource\\%s", worklist[ worklist.Count() - 1 ].String() );
+ }
+
+ char infile[ 512 ];
+ Q_strncpy( infile, outfile, sizeof( infile ) );
+
+ Q_SetExtension( outfile, ".dat", sizeof( outfile ) );
+
+ vprint( 0, "gamedir[ %s ]\n", gamedir );
+ vprint( 0, "infile[ %s ]\n", infile );
+ vprint( 0, "outfile[ %s ]\n", outfile );
+
+ g_pFullFileSystem->AddSearchPath( binaries, "EXECUTABLE_PATH" );
+
+ if ( !g_pVGuiLocalize->AddFile( infile, "MOD", false ) )
+ {
+ Error( "Unable to add localization file '%s'\n", infile );
+ }
+
+ vprint( 0, " Compiling Captions for '%s'...\n", infile );
+
+ CompileCaptionFile( infile, outfile );
+
+ if ( verbose )
+ {
+ DescribeCaptions( outfile );
+ }
+
+ g_pVGuiLocalize->RemoveAll();
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Main entry point
+//-----------------------------------------------------------------------------
+DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( CCompileCaptionsApp )
diff --git a/mp/src/utils/captioncompiler/cbase.h b/mp/src/utils/captioncompiler/cbase.h new file mode 100644 index 00000000..378d503f --- /dev/null +++ b/mp/src/utils/captioncompiler/cbase.h @@ -0,0 +1,19 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef CBASE_H
+#define CBASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetypes.h"
+
+// This is just a dummy file to make this tool compile
+#include "ai_activity.h"
+#include "utlvector.h"
+
+#endif // CBASE_H
diff --git a/mp/src/utils/common/ISQLDBReplyTarget.h b/mp/src/utils/common/ISQLDBReplyTarget.h new file mode 100644 index 00000000..31406368 --- /dev/null +++ b/mp/src/utils/common/ISQLDBReplyTarget.h @@ -0,0 +1,29 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ISQLDLREPLYTARGET_H
+#define ISQLDLREPLYTARGET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Interface to handle results of SQL queries
+//-----------------------------------------------------------------------------
+class ISQLDBReplyTarget
+{
+public:
+ // handles a response from the database
+ virtual void SQLDBResponse(int cmdID, int returnState, int returnVal, void *data) = 0;
+
+ // called from a seperate thread; tells the reply target that a message is waiting for it
+ virtual void WakeUp() = 0;
+
+};
+
+
+#endif // ISQLDLREPLYTARGET_H
diff --git a/mp/src/utils/common/MySqlDatabase.cpp b/mp/src/utils/common/MySqlDatabase.cpp new file mode 100644 index 00000000..46d8a4b9 --- /dev/null +++ b/mp/src/utils/common/MySqlDatabase.cpp @@ -0,0 +1,192 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "MySqlDatabase.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CMySqlDatabase::CMySqlDatabase()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+// blocks until db process thread has stopped
+//-----------------------------------------------------------------------------
+CMySqlDatabase::~CMySqlDatabase()
+{
+ // flag the thread to stop
+ m_bRunThread = false;
+
+ // pulse the thread to make it run
+ ::SetEvent(m_hEvent);
+
+ // make sure it's done
+ ::EnterCriticalSection(&m_csThread);
+ ::LeaveCriticalSection(&m_csThread);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Thread access function
+//-----------------------------------------------------------------------------
+static DWORD WINAPI staticThreadFunc(void *param)
+{
+ ((CMySqlDatabase *)param)->RunThread();
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Establishes connection to the database and sets up this object to handle db command
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMySqlDatabase::Initialize()
+{
+ // prepare critical sections
+ //!! need to download SDK and replace these with InitializeCriticalSectionAndSpinCount() calls
+ ::InitializeCriticalSection(&m_csThread);
+ ::InitializeCriticalSection(&m_csInQueue);
+ ::InitializeCriticalSection(&m_csOutQueue);
+ ::InitializeCriticalSection(&m_csDBAccess);
+
+ // initialize wait calls
+ m_hEvent = ::CreateEvent(NULL, false, true, NULL);
+
+ // start the DB-access thread
+ m_bRunThread = true;
+
+ unsigned long threadID;
+ ::CreateThread(NULL, 0, staticThreadFunc, this, 0, &threadID);
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Main thread loop
+//-----------------------------------------------------------------------------
+void CMySqlDatabase::RunThread()
+{
+ ::EnterCriticalSection(&m_csThread);
+ while (m_bRunThread)
+ {
+ if (m_InQueue.Count() > 0)
+ {
+ // get a dispatched DB request
+ ::EnterCriticalSection(&m_csInQueue);
+
+ // pop the front of the queue
+ int headIndex = m_InQueue.Head();
+ msg_t msg = m_InQueue[headIndex];
+ m_InQueue.Remove(headIndex);
+
+ ::LeaveCriticalSection(&m_csInQueue);
+
+ ::EnterCriticalSection(&m_csDBAccess);
+
+ // run sqldb command
+ msg.result = msg.cmd->RunCommand();
+
+ ::LeaveCriticalSection(&m_csDBAccess);
+
+ if (msg.replyTarget)
+ {
+ // put the results in the outgoing queue
+ ::EnterCriticalSection(&m_csOutQueue);
+ m_OutQueue.AddToTail(msg);
+ ::LeaveCriticalSection(&m_csOutQueue);
+
+ // wake up out queue
+ msg.replyTarget->WakeUp();
+ }
+ else
+ {
+ // there is no return data from the call, so kill the object now
+ msg.cmd->deleteThis();
+ }
+ }
+ else
+ {
+ // nothing in incoming queue, so wait until we get the signal
+ ::WaitForSingleObject(m_hEvent, INFINITE);
+ }
+
+ // check the size of the outqueue; if it's getting too big, sleep to let the main thread catch up
+ if (m_OutQueue.Count() > 50)
+ {
+ ::Sleep(2);
+ }
+ }
+ ::LeaveCriticalSection(&m_csThread);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a database command to the queue, and wakes the db thread
+//-----------------------------------------------------------------------------
+void CMySqlDatabase::AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState)
+{
+ ::EnterCriticalSection(&m_csInQueue);
+
+ // add to the queue
+ msg_t msg = { cmd, replyTarget, 0, returnState };
+ m_InQueue.AddToTail(msg);
+
+ ::LeaveCriticalSection(&m_csInQueue);
+
+ // signal the thread to start running
+ ::SetEvent(m_hEvent);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dispatches responses to SQLDB queries
+//-----------------------------------------------------------------------------
+bool CMySqlDatabase::RunFrame()
+{
+ bool doneWork = false;
+
+ while (m_OutQueue.Count() > 0)
+ {
+ ::EnterCriticalSection(&m_csOutQueue);
+
+ // pop the first item in the queue
+ int headIndex = m_OutQueue.Head();
+ msg_t msg = m_OutQueue[headIndex];
+ m_OutQueue.Remove(headIndex);
+
+ ::LeaveCriticalSection(&m_csOutQueue);
+
+ // run result
+ if (msg.replyTarget)
+ {
+ msg.replyTarget->SQLDBResponse(msg.cmd->GetID(), msg.returnState, msg.result, msg.cmd->GetReturnData());
+
+ // kill command
+ // it would be a good optimization to be able to reuse these
+ msg.cmd->deleteThis();
+ }
+
+ doneWork = true;
+ }
+
+ return doneWork;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: load info - returns the number of sql db queries waiting to be processed
+//-----------------------------------------------------------------------------
+int CMySqlDatabase::QueriesInOutQueue()
+{
+ // the queue names are from the DB point of view, not the server - thus the reversal
+ return m_InQueue.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: number of queries finished processing, waiting to be responded to
+//-----------------------------------------------------------------------------
+int CMySqlDatabase::QueriesInFinishedQueue()
+{
+ return m_OutQueue.Count();
+}
diff --git a/mp/src/utils/common/MySqlDatabase.h b/mp/src/utils/common/MySqlDatabase.h new file mode 100644 index 00000000..caa5855c --- /dev/null +++ b/mp/src/utils/common/MySqlDatabase.h @@ -0,0 +1,104 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MYSQLDATABASE_H
+#define MYSQLDATABASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include <windows.h>
+#include "ISQLDBReplyTarget.h"
+#include "utlvector.h"
+#include "UtlLinkedList.h"
+
+class ISQLDBCommand;
+
+//-----------------------------------------------------------------------------
+// Purpose: Generic MySQL accessing database
+// Provides threaded I/O queue functionality for accessing a mysql db
+//-----------------------------------------------------------------------------
+class CMySqlDatabase
+{
+public:
+ // constructor
+ CMySqlDatabase();
+ ~CMySqlDatabase();
+
+ // initialization - must be called before this object can be used
+ bool Initialize();
+
+ // Dispatches responses to SQLDB queries
+ bool RunFrame();
+
+ // load info - returns the number of sql db queries waiting to be processed
+ virtual int QueriesInOutQueue();
+
+ // number of queries finished processing, waiting to be responded to
+ virtual int QueriesInFinishedQueue();
+
+ // activates the thread
+ void RunThread();
+
+ // command queues
+ void AddCommandToQueue(ISQLDBCommand *cmd, ISQLDBReplyTarget *replyTarget, int returnState = 0);
+
+private:
+
+ // threading data
+ bool m_bRunThread;
+ CRITICAL_SECTION m_csThread;
+ CRITICAL_SECTION m_csInQueue;
+ CRITICAL_SECTION m_csOutQueue;
+ CRITICAL_SECTION m_csDBAccess;
+
+ // wait event
+ HANDLE m_hEvent;
+
+ struct msg_t
+ {
+ ISQLDBCommand *cmd;
+ ISQLDBReplyTarget *replyTarget;
+ int result;
+ int returnState;
+ };
+
+ // command queues
+ CUtlLinkedList<msg_t, int> m_InQueue;
+ CUtlLinkedList<msg_t, int> m_OutQueue;
+};
+
+class Connection;
+
+//-----------------------------------------------------------------------------
+// Purpose: Interface to a command
+//-----------------------------------------------------------------------------
+class ISQLDBCommand
+{
+public:
+ // makes the command run (blocking), returning the success code
+ virtual int RunCommand() = 0;
+
+ // return data
+ virtual void *GetReturnData() { return NULL; }
+
+ // returns the command ID
+ virtual int GetID() { return 0; }
+
+ // gets information about the command for if it failed
+ virtual void GetDebugInfo(char *buf, int bufSize) { buf[0] = 0; }
+
+ // use to delete
+ virtual void deleteThis() = 0;
+
+protected:
+ // protected destructor, so that it has to be deleted through deleteThis()
+ virtual ~ISQLDBCommand() {}
+};
+
+
+#endif // MYSQLDATABASE_H
diff --git a/mp/src/utils/common/bsplib.cpp b/mp/src/utils/common/bsplib.cpp new file mode 100644 index 00000000..84d1a1d0 --- /dev/null +++ b/mp/src/utils/common/bsplib.cpp @@ -0,0 +1,5064 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "bsplib.h"
+#include "zip_utils.h"
+#include "scriplib.h"
+#include "utllinkedlist.h"
+#include "bsptreedata.h"
+#include "cmodel.h"
+#include "gamebspfile.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/hardwareverts.h"
+#include "utlbuffer.h"
+#include "utlrbtree.h"
+#include "utlsymbol.h"
+#include "utlstring.h"
+#include "checksum_crc.h"
+#include "physdll.h"
+#include "tier0/dbg.h"
+#include "lumpfiles.h"
+#include "vtf/vtf.h"
+
+//=============================================================================
+
+// Boundary each lump should be aligned to
+#define LUMP_ALIGNMENT 4
+
+// Data descriptions for byte swapping - only needed
+// for structures that are written to file for use by the game.
+BEGIN_BYTESWAP_DATADESC( dheader_t )
+ DEFINE_FIELD( ident, FIELD_INTEGER ),
+ DEFINE_FIELD( version, FIELD_INTEGER ),
+ DEFINE_EMBEDDED_ARRAY( lumps, HEADER_LUMPS ),
+ DEFINE_FIELD( mapRevision, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( lump_t )
+ DEFINE_FIELD( fileofs, FIELD_INTEGER ),
+ DEFINE_FIELD( filelen, FIELD_INTEGER ),
+ DEFINE_FIELD( version, FIELD_INTEGER ),
+ DEFINE_ARRAY( fourCC, FIELD_CHARACTER, 4 ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dflagslump_t )
+ DEFINE_FIELD( m_LevelFlags, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dplane_t )
+ DEFINE_FIELD( normal, FIELD_VECTOR ),
+ DEFINE_FIELD( dist, FIELD_FLOAT ),
+ DEFINE_FIELD( type, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dleaf_version_0_t )
+ DEFINE_FIELD( contents, FIELD_INTEGER ),
+ DEFINE_FIELD( cluster, FIELD_SHORT ),
+ DEFINE_BITFIELD( bf, FIELD_SHORT, 16 ),
+ DEFINE_ARRAY( mins, FIELD_SHORT, 3 ),
+ DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ),
+ DEFINE_FIELD( firstleafface, FIELD_SHORT ),
+ DEFINE_FIELD( numleaffaces, FIELD_SHORT ),
+ DEFINE_FIELD( firstleafbrush, FIELD_SHORT ),
+ DEFINE_FIELD( numleafbrushes, FIELD_SHORT ),
+ DEFINE_FIELD( leafWaterDataID, FIELD_SHORT ),
+ DEFINE_EMBEDDED( m_AmbientLighting ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dleaf_t )
+ DEFINE_FIELD( contents, FIELD_INTEGER ),
+ DEFINE_FIELD( cluster, FIELD_SHORT ),
+ DEFINE_BITFIELD( bf, FIELD_SHORT, 16 ),
+ DEFINE_ARRAY( mins, FIELD_SHORT, 3 ),
+ DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ),
+ DEFINE_FIELD( firstleafface, FIELD_SHORT ),
+ DEFINE_FIELD( numleaffaces, FIELD_SHORT ),
+ DEFINE_FIELD( firstleafbrush, FIELD_SHORT ),
+ DEFINE_FIELD( numleafbrushes, FIELD_SHORT ),
+ DEFINE_FIELD( leafWaterDataID, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( CompressedLightCube ) // array of 6 ColorRGBExp32 (3 bytes and 1 char)
+ DEFINE_ARRAY( m_Color, FIELD_CHARACTER, 6 * sizeof(ColorRGBExp32) ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dleafambientindex_t )
+ DEFINE_FIELD( ambientSampleCount, FIELD_SHORT ),
+ DEFINE_FIELD( firstAmbientSample, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dleafambientlighting_t ) // array of 6 ColorRGBExp32 (3 bytes and 1 char)
+ DEFINE_EMBEDDED( cube ),
+ DEFINE_FIELD( x, FIELD_CHARACTER ),
+ DEFINE_FIELD( y, FIELD_CHARACTER ),
+ DEFINE_FIELD( z, FIELD_CHARACTER ),
+ DEFINE_FIELD( pad, FIELD_CHARACTER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dvertex_t )
+ DEFINE_FIELD( point, FIELD_VECTOR ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dnode_t )
+ DEFINE_FIELD( planenum, FIELD_INTEGER ),
+ DEFINE_ARRAY( children, FIELD_INTEGER, 2 ),
+ DEFINE_ARRAY( mins, FIELD_SHORT, 3 ),
+ DEFINE_ARRAY( maxs, FIELD_SHORT, 3 ),
+ DEFINE_FIELD( firstface, FIELD_SHORT ),
+ DEFINE_FIELD( numfaces, FIELD_SHORT ),
+ DEFINE_FIELD( area, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( texinfo_t )
+ DEFINE_ARRAY( textureVecsTexelsPerWorldUnits, FIELD_FLOAT, 2 * 4 ),
+ DEFINE_ARRAY( lightmapVecsLuxelsPerWorldUnits, FIELD_FLOAT, 2 * 4 ),
+ DEFINE_FIELD( flags, FIELD_INTEGER ),
+ DEFINE_FIELD( texdata, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dtexdata_t )
+ DEFINE_FIELD( reflectivity, FIELD_VECTOR ),
+ DEFINE_FIELD( nameStringTableID, FIELD_INTEGER ),
+ DEFINE_FIELD( width, FIELD_INTEGER ),
+ DEFINE_FIELD( height, FIELD_INTEGER ),
+ DEFINE_FIELD( view_width, FIELD_INTEGER ),
+ DEFINE_FIELD( view_height, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( ddispinfo_t )
+ DEFINE_FIELD( startPosition, FIELD_VECTOR ),
+ DEFINE_FIELD( m_iDispVertStart, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iDispTriStart, FIELD_INTEGER ),
+ DEFINE_FIELD( power, FIELD_INTEGER ),
+ DEFINE_FIELD( minTess, FIELD_INTEGER ),
+ DEFINE_FIELD( smoothingAngle, FIELD_FLOAT ),
+ DEFINE_FIELD( contents, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iMapFace, FIELD_SHORT ),
+ DEFINE_FIELD( m_iLightmapAlphaStart, FIELD_INTEGER ),
+ DEFINE_FIELD( m_iLightmapSamplePositionStart, FIELD_INTEGER ),
+ DEFINE_EMBEDDED_ARRAY( m_EdgeNeighbors, 4 ),
+ DEFINE_EMBEDDED_ARRAY( m_CornerNeighbors, 4 ),
+ DEFINE_ARRAY( m_AllowedVerts, FIELD_INTEGER, ddispinfo_t::ALLOWEDVERTS_SIZE ), // unsigned long
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( CDispNeighbor )
+ DEFINE_EMBEDDED_ARRAY( m_SubNeighbors, 2 ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( CDispCornerNeighbors )
+ DEFINE_ARRAY( m_Neighbors, FIELD_SHORT, MAX_DISP_CORNER_NEIGHBORS ),
+ DEFINE_FIELD( m_nNeighbors, FIELD_CHARACTER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( CDispSubNeighbor )
+ DEFINE_FIELD( m_iNeighbor, FIELD_SHORT ),
+ DEFINE_FIELD( m_NeighborOrientation, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Span, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_NeighborSpan, FIELD_CHARACTER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( CDispVert )
+ DEFINE_FIELD( m_vVector, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flAlpha, FIELD_FLOAT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( CDispTri )
+ DEFINE_FIELD( m_uiTags, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( CFaceMacroTextureInfo )
+ DEFINE_FIELD( m_MacroTextureNameID, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dprimitive_t )
+ DEFINE_FIELD( type, FIELD_CHARACTER ),
+ DEFINE_FIELD( firstIndex, FIELD_SHORT ),
+ DEFINE_FIELD( indexCount, FIELD_SHORT ),
+ DEFINE_FIELD( firstVert, FIELD_SHORT ),
+ DEFINE_FIELD( vertCount, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dprimvert_t )
+ DEFINE_FIELD( pos, FIELD_VECTOR ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dface_t )
+ DEFINE_FIELD( planenum, FIELD_SHORT ),
+ DEFINE_FIELD( side, FIELD_CHARACTER ),
+ DEFINE_FIELD( onNode, FIELD_CHARACTER ),
+ DEFINE_FIELD( firstedge, FIELD_INTEGER ),
+ DEFINE_FIELD( numedges, FIELD_SHORT ),
+ DEFINE_FIELD( texinfo, FIELD_SHORT ),
+ DEFINE_FIELD( dispinfo, FIELD_SHORT ),
+ DEFINE_FIELD( surfaceFogVolumeID, FIELD_SHORT ),
+ DEFINE_ARRAY( styles, FIELD_CHARACTER, MAXLIGHTMAPS ),
+ DEFINE_FIELD( lightofs, FIELD_INTEGER ),
+ DEFINE_FIELD( area, FIELD_FLOAT ),
+ DEFINE_ARRAY( m_LightmapTextureMinsInLuxels, FIELD_INTEGER, 2 ),
+ DEFINE_ARRAY( m_LightmapTextureSizeInLuxels, FIELD_INTEGER, 2 ),
+ DEFINE_FIELD( origFace, FIELD_INTEGER ),
+ DEFINE_FIELD( m_NumPrims, FIELD_SHORT ),
+ DEFINE_FIELD( firstPrimID, FIELD_SHORT ),
+ DEFINE_FIELD( smoothingGroups, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dfaceid_t )
+ DEFINE_FIELD( hammerfaceid, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dbrush_t )
+ DEFINE_FIELD( firstside, FIELD_INTEGER ),
+ DEFINE_FIELD( numsides, FIELD_INTEGER ),
+ DEFINE_FIELD( contents, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dbrushside_t )
+ DEFINE_FIELD( planenum, FIELD_SHORT ),
+ DEFINE_FIELD( texinfo, FIELD_SHORT ),
+ DEFINE_FIELD( dispinfo, FIELD_SHORT ),
+ DEFINE_FIELD( bevel, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dedge_t )
+ DEFINE_ARRAY( v, FIELD_SHORT, 2 ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dmodel_t )
+ DEFINE_FIELD( mins, FIELD_VECTOR ),
+ DEFINE_FIELD( maxs, FIELD_VECTOR ),
+ DEFINE_FIELD( origin, FIELD_VECTOR ),
+ DEFINE_FIELD( headnode, FIELD_INTEGER ),
+ DEFINE_FIELD( firstface, FIELD_INTEGER ),
+ DEFINE_FIELD( numfaces, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dphysmodel_t )
+ DEFINE_FIELD( modelIndex, FIELD_INTEGER ),
+ DEFINE_FIELD( dataSize, FIELD_INTEGER ),
+ DEFINE_FIELD( keydataSize, FIELD_INTEGER ),
+ DEFINE_FIELD( solidCount, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dphysdisp_t )
+ DEFINE_FIELD( numDisplacements, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( darea_t )
+ DEFINE_FIELD( numareaportals, FIELD_INTEGER ),
+ DEFINE_FIELD( firstareaportal, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dareaportal_t )
+ DEFINE_FIELD( m_PortalKey, FIELD_SHORT ),
+ DEFINE_FIELD( otherarea, FIELD_SHORT ),
+ DEFINE_FIELD( m_FirstClipPortalVert, FIELD_SHORT ),
+ DEFINE_FIELD( m_nClipPortalVerts, FIELD_SHORT ),
+ DEFINE_FIELD( planenum, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dworldlight_t )
+ DEFINE_FIELD( origin, FIELD_VECTOR ),
+ DEFINE_FIELD( intensity, FIELD_VECTOR ),
+ DEFINE_FIELD( normal, FIELD_VECTOR ),
+ DEFINE_FIELD( cluster, FIELD_INTEGER ),
+ DEFINE_FIELD( type, FIELD_INTEGER ), // enumeration
+ DEFINE_FIELD( style, FIELD_INTEGER ),
+ DEFINE_FIELD( stopdot, FIELD_FLOAT ),
+ DEFINE_FIELD( stopdot2, FIELD_FLOAT ),
+ DEFINE_FIELD( exponent, FIELD_FLOAT ),
+ DEFINE_FIELD( radius, FIELD_FLOAT ),
+ DEFINE_FIELD( constant_attn, FIELD_FLOAT ),
+ DEFINE_FIELD( linear_attn, FIELD_FLOAT ),
+ DEFINE_FIELD( quadratic_attn, FIELD_FLOAT ),
+ DEFINE_FIELD( flags, FIELD_INTEGER ),
+ DEFINE_FIELD( texinfo, FIELD_INTEGER ),
+ DEFINE_FIELD( owner, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dleafwaterdata_t )
+ DEFINE_FIELD( surfaceZ, FIELD_FLOAT ),
+ DEFINE_FIELD( minZ, FIELD_FLOAT ),
+ DEFINE_FIELD( surfaceTexInfoID, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( doccluderdata_t )
+ DEFINE_FIELD( flags, FIELD_INTEGER ),
+ DEFINE_FIELD( firstpoly, FIELD_INTEGER ),
+ DEFINE_FIELD( polycount, FIELD_INTEGER ),
+ DEFINE_FIELD( mins, FIELD_VECTOR ),
+ DEFINE_FIELD( maxs, FIELD_VECTOR ),
+ DEFINE_FIELD( area, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( doccluderpolydata_t )
+ DEFINE_FIELD( firstvertexindex, FIELD_INTEGER ),
+ DEFINE_FIELD( vertexcount, FIELD_INTEGER ),
+ DEFINE_FIELD( planenum, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dcubemapsample_t )
+ DEFINE_ARRAY( origin, FIELD_INTEGER, 3 ),
+ DEFINE_FIELD( size, FIELD_CHARACTER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( doverlay_t )
+ DEFINE_FIELD( nId, FIELD_INTEGER ),
+ DEFINE_FIELD( nTexInfo, FIELD_SHORT ),
+ DEFINE_FIELD( m_nFaceCountAndRenderOrder, FIELD_SHORT ),
+ DEFINE_ARRAY( aFaces, FIELD_INTEGER, OVERLAY_BSP_FACE_COUNT ),
+ DEFINE_ARRAY( flU, FIELD_FLOAT, 2 ),
+ DEFINE_ARRAY( flV, FIELD_FLOAT, 2 ),
+ DEFINE_ARRAY( vecUVPoints, FIELD_VECTOR, 4 ),
+ DEFINE_FIELD( vecOrigin, FIELD_VECTOR ),
+ DEFINE_FIELD( vecBasisNormal, FIELD_VECTOR ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dwateroverlay_t )
+ DEFINE_FIELD( nId, FIELD_INTEGER ),
+ DEFINE_FIELD( nTexInfo, FIELD_SHORT ),
+ DEFINE_FIELD( m_nFaceCountAndRenderOrder, FIELD_SHORT ),
+ DEFINE_ARRAY( aFaces, FIELD_INTEGER, WATEROVERLAY_BSP_FACE_COUNT ),
+ DEFINE_ARRAY( flU, FIELD_FLOAT, 2 ),
+ DEFINE_ARRAY( flV, FIELD_FLOAT, 2 ),
+ DEFINE_ARRAY( vecUVPoints, FIELD_VECTOR, 4 ),
+ DEFINE_FIELD( vecOrigin, FIELD_VECTOR ),
+ DEFINE_FIELD( vecBasisNormal, FIELD_VECTOR ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( doverlayfade_t )
+ DEFINE_FIELD( flFadeDistMinSq, FIELD_FLOAT ),
+ DEFINE_FIELD( flFadeDistMaxSq, FIELD_FLOAT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dgamelumpheader_t )
+ DEFINE_FIELD( lumpCount, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( dgamelump_t )
+ DEFINE_FIELD( id, FIELD_INTEGER ), // GameLumpId_t
+ DEFINE_FIELD( flags, FIELD_SHORT ),
+ DEFINE_FIELD( version, FIELD_SHORT ),
+ DEFINE_FIELD( fileofs, FIELD_INTEGER ),
+ DEFINE_FIELD( filelen, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+// From gamebspfile.h
+BEGIN_BYTESWAP_DATADESC( StaticPropDictLump_t )
+ DEFINE_ARRAY( m_Name, FIELD_CHARACTER, STATIC_PROP_NAME_LENGTH ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( StaticPropLump_t )
+ DEFINE_FIELD( m_Origin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle
+ DEFINE_FIELD( m_PropType, FIELD_SHORT ),
+ DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ),
+ DEFINE_FIELD( m_LeafCount, FIELD_SHORT ),
+ DEFINE_FIELD( m_Solid, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Flags, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Skin, FIELD_INTEGER ),
+ DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flForcedFadeScale, FIELD_FLOAT ),
+ DEFINE_FIELD( m_nMinDXLevel, FIELD_SHORT ),
+ DEFINE_FIELD( m_nMaxDXLevel, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( StaticPropLumpV4_t )
+ DEFINE_FIELD( m_Origin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle
+ DEFINE_FIELD( m_PropType, FIELD_SHORT ),
+ DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ),
+ DEFINE_FIELD( m_LeafCount, FIELD_SHORT ),
+ DEFINE_FIELD( m_Solid, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Flags, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Skin, FIELD_INTEGER ),
+ DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( StaticPropLumpV5_t )
+ DEFINE_FIELD( m_Origin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle
+ DEFINE_FIELD( m_PropType, FIELD_SHORT ),
+ DEFINE_FIELD( m_FirstLeaf, FIELD_SHORT ),
+ DEFINE_FIELD( m_LeafCount, FIELD_SHORT ),
+ DEFINE_FIELD( m_Solid, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Flags, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Skin, FIELD_INTEGER ),
+ DEFINE_FIELD( m_FadeMinDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_FadeMaxDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_LightingOrigin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flForcedFadeScale, FIELD_FLOAT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( StaticPropLeafLump_t )
+ DEFINE_FIELD( m_Leaf, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( DetailObjectDictLump_t )
+ DEFINE_ARRAY( m_Name, FIELD_CHARACTER, DETAIL_NAME_LENGTH ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( DetailObjectLump_t )
+ DEFINE_FIELD( m_Origin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Angles, FIELD_VECTOR ), // QAngle
+ DEFINE_FIELD( m_DetailModel, FIELD_SHORT ),
+ DEFINE_FIELD( m_Leaf, FIELD_SHORT ),
+ DEFINE_ARRAY( m_Lighting, FIELD_CHARACTER, 4 ), // ColorRGBExp32
+ DEFINE_FIELD( m_LightStyles, FIELD_INTEGER ),
+ DEFINE_FIELD( m_LightStyleCount, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_SwayAmount, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_ShapeAngle, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_ShapeSize, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_Orientation, FIELD_CHARACTER ),
+ DEFINE_ARRAY( m_Padding2, FIELD_CHARACTER, 3 ),
+ DEFINE_FIELD( m_Type, FIELD_CHARACTER ),
+ DEFINE_ARRAY( m_Padding3, FIELD_CHARACTER, 3 ),
+ DEFINE_FIELD( m_flScale, FIELD_FLOAT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( DetailSpriteDictLump_t )
+ DEFINE_FIELD( m_UL, FIELD_VECTOR2D ),
+ DEFINE_FIELD( m_LR, FIELD_VECTOR2D ),
+ DEFINE_FIELD( m_TexUL, FIELD_VECTOR2D ),
+ DEFINE_FIELD( m_TexLR, FIELD_VECTOR2D ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( DetailPropLightstylesLump_t )
+ DEFINE_ARRAY( m_Lighting, FIELD_CHARACTER, 4 ), // ColorRGBExp32
+ DEFINE_FIELD( m_Style, FIELD_CHARACTER ),
+END_BYTESWAP_DATADESC()
+
+// From vradstaticprops.h
+namespace HardwareVerts
+{
+BEGIN_BYTESWAP_DATADESC( MeshHeader_t )
+ DEFINE_FIELD( m_nLod, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nVertexes, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nOffset, FIELD_INTEGER ),
+ DEFINE_ARRAY( m_nUnused, FIELD_INTEGER, 4 ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC( FileHeader_t )
+ DEFINE_FIELD( m_nVersion, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nChecksum, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nVertexFlags, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nVertexSize, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nVertexes, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nMeshes, FIELD_INTEGER ),
+ DEFINE_ARRAY( m_nUnused, FIELD_INTEGER, 4 ),
+END_BYTESWAP_DATADESC()
+} // end namespace
+
+static const char *s_LumpNames[] = {
+ "LUMP_ENTITIES", // 0
+ "LUMP_PLANES", // 1
+ "LUMP_TEXDATA", // 2
+ "LUMP_VERTEXES", // 3
+ "LUMP_VISIBILITY", // 4
+ "LUMP_NODES", // 5
+ "LUMP_TEXINFO", // 6
+ "LUMP_FACES", // 7
+ "LUMP_LIGHTING", // 8
+ "LUMP_OCCLUSION", // 9
+ "LUMP_LEAFS", // 10
+ "LUMP_FACEIDS", // 11
+ "LUMP_EDGES", // 12
+ "LUMP_SURFEDGES", // 13
+ "LUMP_MODELS", // 14
+ "LUMP_WORLDLIGHTS", // 15
+ "LUMP_LEAFFACES", // 16
+ "LUMP_LEAFBRUSHES", // 17
+ "LUMP_BRUSHES", // 18
+ "LUMP_BRUSHSIDES", // 19
+ "LUMP_AREAS", // 20
+ "LUMP_AREAPORTALS", // 21
+ "LUMP_UNUSED0", // 22
+ "LUMP_UNUSED1", // 23
+ "LUMP_UNUSED2", // 24
+ "LUMP_UNUSED3", // 25
+ "LUMP_DISPINFO", // 26
+ "LUMP_ORIGINALFACES", // 27
+ "LUMP_PHYSDISP", // 28
+ "LUMP_PHYSCOLLIDE", // 29
+ "LUMP_VERTNORMALS", // 30
+ "LUMP_VERTNORMALINDICES", // 31
+ "LUMP_DISP_LIGHTMAP_ALPHAS", // 32
+ "LUMP_DISP_VERTS", // 33
+ "LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS", // 34
+ "LUMP_GAME_LUMP", // 35
+ "LUMP_LEAFWATERDATA", // 36
+ "LUMP_PRIMITIVES", // 37
+ "LUMP_PRIMVERTS", // 38
+ "LUMP_PRIMINDICES", // 39
+ "LUMP_PAKFILE", // 40
+ "LUMP_CLIPPORTALVERTS", // 41
+ "LUMP_CUBEMAPS", // 42
+ "LUMP_TEXDATA_STRING_DATA", // 43
+ "LUMP_TEXDATA_STRING_TABLE", // 44
+ "LUMP_OVERLAYS", // 45
+ "LUMP_LEAFMINDISTTOWATER", // 46
+ "LUMP_FACE_MACRO_TEXTURE_INFO", // 47
+ "LUMP_DISP_TRIS", // 48
+ "LUMP_PHYSCOLLIDESURFACE", // 49
+ "LUMP_WATEROVERLAYS", // 50
+ "LUMP_LEAF_AMBIENT_INDEX_HDR", // 51
+ "LUMP_LEAF_AMBIENT_INDEX", // 52
+ "LUMP_LIGHTING_HDR", // 53
+ "LUMP_WORLDLIGHTS_HDR", // 54
+ "LUMP_LEAF_AMBIENT_LIGHTING_HDR", // 55
+ "LUMP_LEAF_AMBIENT_LIGHTING", // 56
+ "LUMP_XZIPPAKFILE", // 57
+ "LUMP_FACES_HDR", // 58
+ "LUMP_MAP_FLAGS", // 59
+ "LUMP_OVERLAY_FADES", // 60
+};
+
+const char *GetLumpName( unsigned int lumpnum )
+{
+ if ( lumpnum >= ARRAYSIZE( s_LumpNames ) )
+ {
+ return "UNKNOWN";
+ }
+ return s_LumpNames[lumpnum];
+}
+
+// "-hdr" tells us to use the HDR fields (if present) on the light sources. Also, tells us to write
+// out the HDR lumps for lightmaps, ambient leaves, and lights sources.
+bool g_bHDR = false;
+
+// Set to true to generate Xbox360 native output files
+static bool g_bSwapOnLoad = false;
+static bool g_bSwapOnWrite = false;
+
+VTFConvertFunc_t g_pVTFConvertFunc;
+VHVFixupFunc_t g_pVHVFixupFunc;
+CompressFunc_t g_pCompressFunc;
+
+CUtlVector< CUtlString > g_StaticPropNames;
+CUtlVector< int > g_StaticPropInstances;
+
+CByteswap g_Swap;
+
+uint32 g_LevelFlags = 0;
+
+int nummodels;
+dmodel_t dmodels[MAX_MAP_MODELS];
+
+int visdatasize;
+byte dvisdata[MAX_MAP_VISIBILITY];
+dvis_t *dvis = (dvis_t *)dvisdata;
+
+CUtlVector<byte> dlightdataHDR;
+CUtlVector<byte> dlightdataLDR;
+CUtlVector<byte> *pdlightdata = &dlightdataLDR;
+
+CUtlVector<char> dentdata;
+
+int numleafs;
+#if !defined( BSP_USE_LESS_MEMORY )
+dleaf_t dleafs[MAX_MAP_LEAFS];
+#else
+dleaf_t *dleafs;
+#endif
+
+CUtlVector<dleafambientindex_t> g_LeafAmbientIndexLDR;
+CUtlVector<dleafambientindex_t> g_LeafAmbientIndexHDR;
+CUtlVector<dleafambientindex_t> *g_pLeafAmbientIndex = NULL;
+CUtlVector<dleafambientlighting_t> g_LeafAmbientLightingLDR;
+CUtlVector<dleafambientlighting_t> g_LeafAmbientLightingHDR;
+CUtlVector<dleafambientlighting_t> *g_pLeafAmbientLighting = NULL;
+
+unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS];
+
+int numplanes;
+dplane_t dplanes[MAX_MAP_PLANES];
+
+int numvertexes;
+dvertex_t dvertexes[MAX_MAP_VERTS];
+
+int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals.
+unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS];
+
+int g_numvertnormals;
+Vector g_vertnormals[MAX_MAP_VERTNORMALS];
+
+int numnodes;
+dnode_t dnodes[MAX_MAP_NODES];
+
+CUtlVector<texinfo_t> texinfo( MAX_MAP_TEXINFO );
+
+int numtexdata;
+dtexdata_t dtexdata[MAX_MAP_TEXDATA];
+
+//
+// displacement map bsp file info: dispinfo
+//
+CUtlVector<ddispinfo_t> g_dispinfo;
+CUtlVector<CDispVert> g_DispVerts;
+CUtlVector<CDispTri> g_DispTris;
+CUtlVector<unsigned char> g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS
+
+int numorigfaces;
+dface_t dorigfaces[MAX_MAP_FACES];
+
+int g_numprimitives = 0;
+dprimitive_t g_primitives[MAX_MAP_PRIMITIVES];
+
+int g_numprimverts = 0;
+dprimvert_t g_primverts[MAX_MAP_PRIMVERTS];
+
+int g_numprimindices = 0;
+unsigned short g_primindices[MAX_MAP_PRIMINDICES];
+
+int numfaces;
+dface_t dfaces[MAX_MAP_FACES];
+
+int numfaceids;
+CUtlVector<dfaceid_t> dfaceids;
+
+int numfaces_hdr;
+dface_t dfaces_hdr[MAX_MAP_FACES];
+
+int numedges;
+dedge_t dedges[MAX_MAP_EDGES];
+
+int numleaffaces;
+unsigned short dleaffaces[MAX_MAP_LEAFFACES];
+
+int numleafbrushes;
+unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+int numsurfedges;
+int dsurfedges[MAX_MAP_SURFEDGES];
+
+int numbrushes;
+dbrush_t dbrushes[MAX_MAP_BRUSHES];
+
+int numbrushsides;
+dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
+
+int numareas;
+darea_t dareas[MAX_MAP_AREAS];
+
+int numareaportals;
+dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
+
+int numworldlightsLDR;
+dworldlight_t dworldlightsLDR[MAX_MAP_WORLDLIGHTS];
+
+int numworldlightsHDR;
+dworldlight_t dworldlightsHDR[MAX_MAP_WORLDLIGHTS];
+
+int *pNumworldlights = &numworldlightsLDR;
+dworldlight_t *dworldlights = dworldlightsLDR;
+
+int numleafwaterdata = 0;
+dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA];
+
+CUtlVector<CFaceMacroTextureInfo> g_FaceMacroTextureInfos;
+
+Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS];
+int g_nClipPortalVerts;
+
+dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES];
+int g_nCubemapSamples = 0;
+
+int g_nOverlayCount;
+doverlay_t g_Overlays[MAX_MAP_OVERLAYS];
+doverlayfade_t g_OverlayFades[MAX_MAP_OVERLAYS];
+
+int g_nWaterOverlayCount;
+dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS];
+
+CUtlVector<char> g_TexDataStringData;
+CUtlVector<int> g_TexDataStringTable;
+
+byte *g_pPhysCollide = NULL;
+int g_PhysCollideSize = 0;
+int g_MapRevision = 0;
+
+byte *g_pPhysDisp = NULL;
+int g_PhysDispSize = 0;
+
+CUtlVector<doccluderdata_t> g_OccluderData( 256, 256 );
+CUtlVector<doccluderpolydata_t> g_OccluderPolyData( 1024, 1024 );
+CUtlVector<int> g_OccluderVertexIndices( 2048, 2048 );
+
+template <class T> static void WriteData( T *pData, int count = 1 );
+template <class T> static void WriteData( int fieldType, T *pData, int count = 1 );
+template< class T > static void AddLump( int lumpnum, T *pData, int count, int version = 0 );
+template< class T > static void AddLump( int lumpnum, CUtlVector<T> &data, int version = 0 );
+
+dheader_t *g_pBSPHeader;
+FileHandle_t g_hBSPFile;
+
+struct Lump_t
+{
+ void *pLumps[HEADER_LUMPS];
+ int size[HEADER_LUMPS];
+ bool bLumpParsed[HEADER_LUMPS];
+} g_Lumps;
+
+CGameLump g_GameLumps;
+
+static IZip *s_pakFile = 0;
+
+//-----------------------------------------------------------------------------
+// Keep the file position aligned to an arbitrary boundary.
+// Returns updated file position.
+//-----------------------------------------------------------------------------
+static unsigned int AlignFilePosition( FileHandle_t hFile, int alignment )
+{
+ unsigned int currPosition = g_pFileSystem->Tell( hFile );
+
+ if ( alignment >= 2 )
+ {
+ unsigned int newPosition = AlignValue( currPosition, alignment );
+ unsigned int count = newPosition - currPosition;
+ if ( count )
+ {
+ char *pBuffer;
+ char smallBuffer[4096];
+ if ( count > sizeof( smallBuffer ) )
+ {
+ pBuffer = (char *)malloc( count );
+ }
+ else
+ {
+ pBuffer = smallBuffer;
+ }
+
+ memset( pBuffer, 0, count );
+ SafeWrite( hFile, pBuffer, count );
+
+ if ( pBuffer != smallBuffer )
+ {
+ free( pBuffer );
+ }
+
+ currPosition = newPosition;
+ }
+ }
+
+ return currPosition;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: // Get a pakfile instance
+// Output : IZip*
+//-----------------------------------------------------------------------------
+IZip* GetPakFile( void )
+{
+ if ( !s_pakFile )
+ {
+ s_pakFile = IZip::CreateZip();
+ }
+ return s_pakFile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Free the pak files
+//-----------------------------------------------------------------------------
+void ReleasePakFileLumps( void )
+{
+ // Release the pak files
+ IZip::ReleaseZip( s_pakFile );
+ s_pakFile = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the sector alignment for all subsequent zip operations
+//-----------------------------------------------------------------------------
+void ForceAlignment( IZip *pak, bool bAlign, bool bCompatibleFormat, unsigned int alignmentSize )
+{
+ pak->ForceAlignment( bAlign, bCompatibleFormat, alignmentSize );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Store data back out to .bsp file
+//-----------------------------------------------------------------------------
+static void WritePakFileLump( void )
+{
+ CUtlBuffer buf( 0, 0 );
+ GetPakFile()->ActivateByteSwapping( IsX360() );
+ GetPakFile()->SaveToBuffer( buf );
+
+ // must respect pak file alignment
+ // pad up and ensure lump starts on same aligned boundary
+ AlignFilePosition( g_hBSPFile, GetPakFile()->GetAlignment() );
+
+ // Now store final buffers out to file
+ AddLump( LUMP_PAKFILE, (byte*)buf.Base(), buf.TellPut() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove all entries
+//-----------------------------------------------------------------------------
+void ClearPakFile( IZip *pak )
+{
+ pak->Reset();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add file from disk to .bsp PAK lump
+// Input : *relativename -
+// *fullpath -
+//-----------------------------------------------------------------------------
+void AddFileToPak( IZip *pak, const char *relativename, const char *fullpath )
+{
+ pak->AddFileToZip( relativename, fullpath );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add buffer to .bsp PAK lump as named file
+// Input : *relativename -
+// *data -
+// length -
+//-----------------------------------------------------------------------------
+void AddBufferToPak( IZip *pak, const char *pRelativeName, void *data, int length, bool bTextMode )
+{
+ pak->AddBufferToZip( pRelativeName, data, length, bTextMode );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if a file already exists in the pack file.
+// Input : *relativename -
+//-----------------------------------------------------------------------------
+bool FileExistsInPak( IZip *pak, const char *pRelativeName )
+{
+ return pak->FileExistsInZip( pRelativeName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Read a file from the pack file
+//-----------------------------------------------------------------------------
+bool ReadFileFromPak( IZip *pak, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf )
+{
+ return pak->ReadFileFromZip( pRelativeName, bTextMode, buf );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove file from .bsp PAK lump
+// Input : *relativename -
+//-----------------------------------------------------------------------------
+void RemoveFileFromPak( IZip *pak, const char *relativename )
+{
+ pak->RemoveFileFromZip( relativename );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Get next filename in directory
+// Input : id, -1 to start, returns next id, or -1 at list conclusion
+//-----------------------------------------------------------------------------
+int GetNextFilename( IZip *pak, int id, char *pBuffer, int bufferSize, int &fileSize )
+{
+ return pak->GetNextFilename( id, pBuffer, bufferSize, fileSize );
+}
+
+//-----------------------------------------------------------------------------
+// Convert four-CC code to a handle + back
+//-----------------------------------------------------------------------------
+GameLumpHandle_t CGameLump::GetGameLumpHandle( GameLumpId_t id )
+{
+ // NOTE: I'm also expecting game lump id's to be four-CC codes
+ Assert( id > HEADER_LUMPS );
+
+ FOR_EACH_LL(m_GameLumps, i)
+ {
+ if (m_GameLumps[i].m_Id == id)
+ return i;
+ }
+
+ return InvalidGameLump();
+}
+
+GameLumpId_t CGameLump::GetGameLumpId( GameLumpHandle_t handle )
+{
+ return m_GameLumps[handle].m_Id;
+}
+
+int CGameLump::GetGameLumpFlags( GameLumpHandle_t handle )
+{
+ return m_GameLumps[handle].m_Flags;
+}
+
+int CGameLump::GetGameLumpVersion( GameLumpHandle_t handle )
+{
+ return m_GameLumps[handle].m_Version;
+}
+
+
+//-----------------------------------------------------------------------------
+// Game lump accessor methods
+//-----------------------------------------------------------------------------
+
+void* CGameLump::GetGameLump( GameLumpHandle_t id )
+{
+ return m_GameLumps[id].m_Memory.Base();
+}
+
+int CGameLump::GameLumpSize( GameLumpHandle_t id )
+{
+ return m_GameLumps[id].m_Memory.NumAllocated();
+}
+
+
+//-----------------------------------------------------------------------------
+// Game lump iteration methods
+//-----------------------------------------------------------------------------
+
+GameLumpHandle_t CGameLump::FirstGameLump()
+{
+ return (m_GameLumps.Count()) ? m_GameLumps.Head() : InvalidGameLump();
+}
+
+GameLumpHandle_t CGameLump::NextGameLump( GameLumpHandle_t handle )
+{
+
+ return (m_GameLumps.IsValidIndex(handle)) ? m_GameLumps.Next(handle) : InvalidGameLump();
+}
+
+GameLumpHandle_t CGameLump::InvalidGameLump()
+{
+ return 0xFFFF;
+}
+
+
+//-----------------------------------------------------------------------------
+// Game lump creation/destruction method
+//-----------------------------------------------------------------------------
+
+GameLumpHandle_t CGameLump::CreateGameLump( GameLumpId_t id, int size, int flags, int version )
+{
+ Assert( GetGameLumpHandle(id) == InvalidGameLump() );
+ GameLumpHandle_t handle = m_GameLumps.AddToTail();
+ m_GameLumps[handle].m_Id = id;
+ m_GameLumps[handle].m_Flags = flags;
+ m_GameLumps[handle].m_Version = version;
+ m_GameLumps[handle].m_Memory.EnsureCapacity( size );
+ return handle;
+}
+
+void CGameLump::DestroyGameLump( GameLumpHandle_t handle )
+{
+ m_GameLumps.Remove( handle );
+}
+
+void CGameLump::DestroyAllGameLumps()
+{
+ m_GameLumps.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Compute file size and clump count
+//-----------------------------------------------------------------------------
+
+void CGameLump::ComputeGameLumpSizeAndCount( int& size, int& clumpCount )
+{
+ // Figure out total size of the client lumps
+ size = 0;
+ clumpCount = 0;
+ GameLumpHandle_t h;
+ for( h = FirstGameLump(); h != InvalidGameLump(); h = NextGameLump( h ) )
+ {
+ ++clumpCount;
+ size += GameLumpSize( h );
+ }
+
+ // Add on headers
+ size += sizeof( dgamelumpheader_t ) + clumpCount * sizeof( dgamelump_t );
+}
+
+
+void CGameLump::SwapGameLump( GameLumpId_t id, int version, byte *dest, byte *src, int length )
+{
+ int count = 0;
+ switch( id )
+ {
+ case GAMELUMP_STATIC_PROPS:
+ // Swap the static prop model dict
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ g_Swap.SwapFieldsToTargetEndian( (StaticPropDictLump_t*)dest, (StaticPropDictLump_t*)src, count );
+ src += sizeof(StaticPropDictLump_t) * count;
+ dest += sizeof(StaticPropDictLump_t) * count;
+
+ // Swap the leaf list
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ g_Swap.SwapFieldsToTargetEndian( (StaticPropLeafLump_t*)dest, (StaticPropLeafLump_t*)src, count );
+ src += sizeof(StaticPropLeafLump_t) * count;
+ dest += sizeof(StaticPropLeafLump_t) * count;
+
+ // Swap the models
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ // The one-at-a-time swap is to compensate for these structures
+ // possibly being misaligned, which crashes the Xbox 360.
+ if ( version == 4 )
+ {
+ StaticPropLumpV4_t lump;
+ for ( int i = 0; i < count; ++i )
+ {
+ Q_memcpy( &lump, src, sizeof(StaticPropLumpV4_t) );
+ g_Swap.SwapFieldsToTargetEndian( &lump, &lump );
+ Q_memcpy( dest, &lump, sizeof(StaticPropLumpV4_t) );
+ src += sizeof( StaticPropLumpV4_t );
+ dest += sizeof( StaticPropLumpV4_t );
+ }
+ }
+ else if ( version == 5 )
+ {
+ StaticPropLumpV5_t lump;
+ for ( int i = 0; i < count; ++i )
+ {
+ Q_memcpy( &lump, src, sizeof(StaticPropLumpV5_t) );
+ g_Swap.SwapFieldsToTargetEndian( &lump, &lump );
+ Q_memcpy( dest, &lump, sizeof(StaticPropLumpV5_t) );
+ src += sizeof( StaticPropLumpV5_t );
+ dest += sizeof( StaticPropLumpV5_t );
+ }
+ }
+ else
+ {
+ if ( version != 6 )
+ {
+ Error( "Unknown Static Prop Lump version %d didn't get swapped!\n", version );
+ }
+
+ StaticPropLump_t lump;
+ for ( int i = 0; i < count; ++i )
+ {
+ Q_memcpy( &lump, src, sizeof(StaticPropLump_t) );
+ g_Swap.SwapFieldsToTargetEndian( &lump, &lump );
+ Q_memcpy( dest, &lump, sizeof(StaticPropLump_t) );
+ src += sizeof( StaticPropLump_t );
+ dest += sizeof( StaticPropLump_t );
+ }
+ }
+ break;
+
+ case GAMELUMP_DETAIL_PROPS:
+ // Swap the detail prop model dict
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ g_Swap.SwapFieldsToTargetEndian( (DetailObjectDictLump_t*)dest, (DetailObjectDictLump_t*)src, count );
+ src += sizeof(DetailObjectDictLump_t) * count;
+ dest += sizeof(DetailObjectDictLump_t) * count;
+
+ if ( version == 4 )
+ {
+ // Swap the detail sprite dict
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ DetailSpriteDictLump_t spritelump;
+ for ( int i = 0; i < count; ++i )
+ {
+ Q_memcpy( &spritelump, src, sizeof(DetailSpriteDictLump_t) );
+ g_Swap.SwapFieldsToTargetEndian( &spritelump, &spritelump );
+ Q_memcpy( dest, &spritelump, sizeof(DetailSpriteDictLump_t) );
+ src += sizeof(DetailSpriteDictLump_t);
+ dest += sizeof(DetailSpriteDictLump_t);
+ }
+
+ // Swap the models
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ DetailObjectLump_t objectlump;
+ for ( int i = 0; i < count; ++i )
+ {
+ Q_memcpy( &objectlump, src, sizeof(DetailObjectLump_t) );
+ g_Swap.SwapFieldsToTargetEndian( &objectlump, &objectlump );
+ Q_memcpy( dest, &objectlump, sizeof(DetailObjectLump_t) );
+ src += sizeof(DetailObjectLump_t);
+ dest += sizeof(DetailObjectLump_t);
+ }
+ }
+ break;
+
+ case GAMELUMP_DETAIL_PROP_LIGHTING:
+ // Swap the LDR light styles
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ g_Swap.SwapFieldsToTargetEndian( (DetailPropLightstylesLump_t*)dest, (DetailPropLightstylesLump_t*)src, count );
+ src += sizeof(DetailObjectDictLump_t) * count;
+ dest += sizeof(DetailObjectDictLump_t) * count;
+ break;
+
+ case GAMELUMP_DETAIL_PROP_LIGHTING_HDR:
+ // Swap the HDR light styles
+ count = *(int*)src;
+ g_Swap.SwapBufferToTargetEndian( (int*)dest, (int*)src );
+ count = g_bSwapOnLoad ? *(int*)dest : count;
+ src += sizeof(int);
+ dest += sizeof(int);
+
+ g_Swap.SwapFieldsToTargetEndian( (DetailPropLightstylesLump_t*)dest, (DetailPropLightstylesLump_t*)src, count );
+ src += sizeof(DetailObjectDictLump_t) * count;
+ dest += sizeof(DetailObjectDictLump_t) * count;
+ break;
+
+ default:
+ char idchars[5] = {0};
+ Q_memcpy( idchars, &id, 4 );
+ Warning( "Unknown game lump '%s' didn't get swapped!\n", idchars );
+ memcpy ( dest, src, length);
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Game lump file I/O
+//-----------------------------------------------------------------------------
+void CGameLump::ParseGameLump( dheader_t* pHeader )
+{
+ g_GameLumps.DestroyAllGameLumps();
+
+ g_Lumps.bLumpParsed[LUMP_GAME_LUMP] = true;
+
+ int length = pHeader->lumps[LUMP_GAME_LUMP].filelen;
+ int ofs = pHeader->lumps[LUMP_GAME_LUMP].fileofs;
+
+ if (length > 0)
+ {
+ // Read dictionary...
+ dgamelumpheader_t* pGameLumpHeader = (dgamelumpheader_t*)((byte *)pHeader + ofs);
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapFieldsToTargetEndian( pGameLumpHeader );
+ }
+ dgamelump_t* pGameLump = (dgamelump_t*)(pGameLumpHeader + 1);
+ for (int i = 0; i < pGameLumpHeader->lumpCount; ++i )
+ {
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapFieldsToTargetEndian( &pGameLump[i] );
+ }
+
+ int length = pGameLump[i].filelen;
+ GameLumpHandle_t lump = g_GameLumps.CreateGameLump( pGameLump[i].id, length, pGameLump[i].flags, pGameLump[i].version );
+ if ( g_bSwapOnLoad )
+ {
+ SwapGameLump( pGameLump[i].id, pGameLump[i].version, (byte*)g_GameLumps.GetGameLump(lump), (byte *)pHeader + pGameLump[i].fileofs, length );
+ }
+ else
+ {
+ memcpy( g_GameLumps.GetGameLump(lump), (byte *)pHeader + pGameLump[i].fileofs, length );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// String table methods
+//-----------------------------------------------------------------------------
+const char *TexDataStringTable_GetString( int stringID )
+{
+ return &g_TexDataStringData[g_TexDataStringTable[stringID]];
+}
+
+int TexDataStringTable_AddOrFindString( const char *pString )
+{
+ int i;
+ // garymcthack: Make this use an RBTree!
+ for( i = 0; i < g_TexDataStringTable.Count(); i++ )
+ {
+ if( stricmp( pString, &g_TexDataStringData[g_TexDataStringTable[i]] ) == 0 )
+ {
+ return i;
+ }
+ }
+
+ int len = strlen( pString );
+ int outOffset = g_TexDataStringData.AddMultipleToTail( len+1, pString );
+ int outIndex = g_TexDataStringTable.AddToTail( outOffset );
+ return outIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Adds all game lumps into one big block
+//-----------------------------------------------------------------------------
+
+static void AddGameLumps( )
+{
+ // Figure out total size of the client lumps
+ int size, clumpCount;
+ g_GameLumps.ComputeGameLumpSizeAndCount( size, clumpCount );
+
+ // Set up the main lump dictionary entry
+ g_Lumps.size[LUMP_GAME_LUMP] = 0; // mark it written
+
+ lump_t* lump = &g_pBSPHeader->lumps[LUMP_GAME_LUMP];
+
+ lump->fileofs = g_pFileSystem->Tell( g_hBSPFile );
+ lump->filelen = size;
+
+ // write header
+ dgamelumpheader_t header;
+ header.lumpCount = clumpCount;
+ WriteData( &header );
+
+ // write dictionary
+ dgamelump_t dict;
+ int offset = lump->fileofs + sizeof(header) + clumpCount * sizeof(dgamelump_t);
+ GameLumpHandle_t h;
+ for( h = g_GameLumps.FirstGameLump(); h != g_GameLumps.InvalidGameLump(); h = g_GameLumps.NextGameLump( h ) )
+ {
+ dict.id = g_GameLumps.GetGameLumpId(h);
+ dict.version = g_GameLumps.GetGameLumpVersion(h);
+ dict.flags = g_GameLumps.GetGameLumpFlags(h);
+ dict.fileofs = offset;
+ dict.filelen = g_GameLumps.GameLumpSize( h );
+ offset += dict.filelen;
+
+ WriteData( &dict );
+ }
+
+ // write lumps..
+ for( h = g_GameLumps.FirstGameLump(); h != g_GameLumps.InvalidGameLump(); h = g_GameLumps.NextGameLump( h ) )
+ {
+ unsigned int lumpsize = g_GameLumps.GameLumpSize(h);
+ if ( g_bSwapOnWrite )
+ {
+ g_GameLumps.SwapGameLump( g_GameLumps.GetGameLumpId(h), g_GameLumps.GetGameLumpVersion(h), (byte*)g_GameLumps.GetGameLump(h), (byte*)g_GameLumps.GetGameLump(h), lumpsize );
+ }
+ SafeWrite( g_hBSPFile, g_GameLumps.GetGameLump(h), lumpsize );
+ }
+
+ // align to doubleword
+ AlignFilePosition( g_hBSPFile, 4 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds the occluder lump...
+//-----------------------------------------------------------------------------
+static void AddOcclusionLump( )
+{
+ g_Lumps.size[LUMP_OCCLUSION] = 0; // mark it written
+
+ int nOccluderCount = g_OccluderData.Count();
+ int nOccluderPolyDataCount = g_OccluderPolyData.Count();
+ int nOccluderVertexIndices = g_OccluderVertexIndices.Count();
+
+ int nLumpLength = nOccluderCount * sizeof(doccluderdata_t) +
+ nOccluderPolyDataCount * sizeof(doccluderpolydata_t) +
+ nOccluderVertexIndices * sizeof(int) +
+ 3 * sizeof(int);
+
+ lump_t *lump = &g_pBSPHeader->lumps[LUMP_OCCLUSION];
+
+ lump->fileofs = g_pFileSystem->Tell( g_hBSPFile );
+ lump->filelen = nLumpLength;
+ lump->version = LUMP_OCCLUSION_VERSION;
+ lump->fourCC[0] = ( char )0;
+ lump->fourCC[1] = ( char )0;
+ lump->fourCC[2] = ( char )0;
+ lump->fourCC[3] = ( char )0;
+
+ // Data is swapped in place, so the 'Count' variables aren't safe to use after they're written
+ WriteData( FIELD_INTEGER, &nOccluderCount );
+ WriteData( (doccluderdata_t*)g_OccluderData.Base(), g_OccluderData.Count() );
+ WriteData( FIELD_INTEGER, &nOccluderPolyDataCount );
+ WriteData( (doccluderpolydata_t*)g_OccluderPolyData.Base(), g_OccluderPolyData.Count() );
+ WriteData( FIELD_INTEGER, &nOccluderVertexIndices );
+ WriteData( FIELD_INTEGER, (int*)g_OccluderVertexIndices.Base(), g_OccluderVertexIndices.Count() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the occluder lump...
+//-----------------------------------------------------------------------------
+static void UnserializeOcclusionLumpV2( CUtlBuffer &buf )
+{
+ int nCount = buf.GetInt();
+ if ( nCount )
+ {
+ g_OccluderData.SetCount( nCount );
+ buf.GetObjects( g_OccluderData.Base(), nCount );
+ }
+
+ nCount = buf.GetInt();
+ if ( nCount )
+ {
+ g_OccluderPolyData.SetCount( nCount );
+ buf.GetObjects( g_OccluderPolyData.Base(), nCount );
+ }
+
+ nCount = buf.GetInt();
+ if ( nCount )
+ {
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapBufferToTargetEndian( (int*)buf.PeekGet(), (int*)buf.PeekGet(), nCount );
+ }
+ g_OccluderVertexIndices.SetCount( nCount );
+ buf.Get( g_OccluderVertexIndices.Base(), nCount * sizeof(g_OccluderVertexIndices[0]) );
+ }
+}
+
+
+static void LoadOcclusionLump()
+{
+ g_OccluderData.RemoveAll();
+ g_OccluderPolyData.RemoveAll();
+ g_OccluderVertexIndices.RemoveAll();
+
+ int length, ofs;
+
+ g_Lumps.bLumpParsed[LUMP_OCCLUSION] = true;
+
+ length = g_pBSPHeader->lumps[LUMP_OCCLUSION].filelen;
+ ofs = g_pBSPHeader->lumps[LUMP_OCCLUSION].fileofs;
+
+ CUtlBuffer buf( (byte *)g_pBSPHeader + ofs, length, CUtlBuffer::READ_ONLY );
+ buf.ActivateByteSwapping( g_bSwapOnLoad );
+ switch ( g_pBSPHeader->lumps[LUMP_OCCLUSION].version )
+ {
+ case 2:
+ UnserializeOcclusionLumpV2( buf );
+ break;
+
+ case 0:
+ break;
+
+ default:
+ Error("Unknown occlusion lump version!\n");
+ break;
+ }
+}
+
+
+/*
+===============
+CompressVis
+
+===============
+*/
+int CompressVis (byte *vis, byte *dest)
+{
+ int j;
+ int rep;
+ int visrow;
+ byte *dest_p;
+
+ dest_p = dest;
+// visrow = (r_numvisleafs + 7)>>3;
+ visrow = (dvis->numclusters + 7)>>3;
+
+ for (j=0 ; j<visrow ; j++)
+ {
+ *dest_p++ = vis[j];
+ if (vis[j])
+ continue;
+
+ rep = 1;
+ for ( j++; j<visrow ; j++)
+ if (vis[j] || rep == 255)
+ break;
+ else
+ rep++;
+ *dest_p++ = rep;
+ j--;
+ }
+
+ return dest_p - dest;
+}
+
+
+/*
+===================
+DecompressVis
+===================
+*/
+void DecompressVis (byte *in, byte *decompressed)
+{
+ int c;
+ byte *out;
+ int row;
+
+// row = (r_numvisleafs+7)>>3;
+ row = (dvis->numclusters+7)>>3;
+ out = decompressed;
+
+ do
+ {
+ if (*in)
+ {
+ *out++ = *in++;
+ continue;
+ }
+
+ c = in[1];
+ if (!c)
+ Error ("DecompressVis: 0 repeat");
+ in += 2;
+ if ((out - decompressed) + c > row)
+ {
+ c = row - (out - decompressed);
+ Warning( "warning: Vis decompression overrun\n" );
+ }
+ while (c)
+ {
+ *out++ = 0;
+ c--;
+ }
+ } while (out - decompressed < row);
+}
+
+//-----------------------------------------------------------------------------
+// Lump-specific swap functions
+//-----------------------------------------------------------------------------
+struct swapcollideheader_t
+{
+ DECLARE_BYTESWAP_DATADESC();
+ int size;
+ int vphysicsID;
+ short version;
+ short modelType;
+};
+
+struct swapcompactsurfaceheader_t : swapcollideheader_t
+{
+ DECLARE_BYTESWAP_DATADESC();
+ int surfaceSize;
+ Vector dragAxisAreas;
+ int axisMapSize;
+};
+
+struct swapmoppsurfaceheader_t : swapcollideheader_t
+{
+ DECLARE_BYTESWAP_DATADESC();
+ int moppSize;
+};
+
+BEGIN_BYTESWAP_DATADESC( swapcollideheader_t )
+ DEFINE_FIELD( size, FIELD_INTEGER ),
+ DEFINE_FIELD( vphysicsID, FIELD_INTEGER ),
+ DEFINE_FIELD( version, FIELD_SHORT ),
+ DEFINE_FIELD( modelType, FIELD_SHORT ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC_( swapcompactsurfaceheader_t, swapcollideheader_t )
+ DEFINE_FIELD( surfaceSize, FIELD_INTEGER ),
+ DEFINE_FIELD( dragAxisAreas, FIELD_VECTOR ),
+ DEFINE_FIELD( axisMapSize, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+BEGIN_BYTESWAP_DATADESC_( swapmoppsurfaceheader_t, swapcollideheader_t )
+ DEFINE_FIELD( moppSize, FIELD_INTEGER ),
+END_BYTESWAP_DATADESC()
+
+
+static void SwapPhyscollideLump( byte *pDestBase, byte *pSrcBase, unsigned int &count )
+{
+ IPhysicsCollision *physcollision = NULL;
+ CSysModule *pPhysicsModule = g_pFullFileSystem->LoadModule( "vphysics.dll" );
+ if ( pPhysicsModule )
+ {
+ CreateInterfaceFn physicsFactory = Sys_GetFactory( pPhysicsModule );
+ if ( physicsFactory )
+ {
+ physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ }
+ }
+
+ if ( !physcollision )
+ {
+ Warning("!!! WARNING: Can't swap the physcollide lump!\n" );
+ return;
+ }
+
+ // physics data is variable length. The last physmodel is a NULL pointer
+ // with modelIndex -1, dataSize -1
+ dphysmodel_t *pPhysModel;
+ byte *pSrc = pSrcBase;
+
+ // first the src chunks have to be aligned properly
+ // swap increases size, allocate enough expansion room
+ byte *pSrcAlignedBase = (byte*)malloc( 2*count );
+ byte *basePtr = pSrcAlignedBase;
+ byte *pSrcAligned = pSrcAlignedBase;
+
+ do
+ {
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapFieldsToTargetEndian( (dphysmodel_t*)pSrcAligned, (dphysmodel_t*)pSrc );
+ }
+ else
+ {
+ Q_memcpy( pSrcAligned, pSrc, sizeof(dphysmodel_t) );
+ }
+ pPhysModel = (dphysmodel_t*)pSrcAligned;
+
+ pSrc += sizeof(dphysmodel_t);
+ pSrcAligned += sizeof(dphysmodel_t);
+
+ if ( pPhysModel->dataSize > 0 )
+ {
+ // Align the collide headers
+ for ( int i = 0; i < pPhysModel->solidCount; ++i )
+ {
+ // Get data size
+ int size;
+ Q_memcpy( &size, pSrc, sizeof(int) );
+ if ( g_bSwapOnLoad )
+ size = SwapLong( size );
+
+ // Fixup size
+ int padBytes = 0;
+ if ( size % 4 != 0 )
+ {
+ padBytes = ( 4 - size % 4 );
+ count += padBytes;
+ pPhysModel->dataSize += padBytes;
+ }
+
+ // Copy data and size into alligned buffer
+ int newsize = size + padBytes;
+ if ( g_bSwapOnLoad )
+ newsize = SwapLong( newsize );
+
+ Q_memcpy( pSrcAligned, &newsize, sizeof(int) );
+ Q_memcpy( pSrcAligned + sizeof(int), pSrc + sizeof(int), size );
+ pSrcAligned += size + padBytes + sizeof(int);
+ pSrc += size + sizeof(int);
+ }
+
+ int padBytes = 0;
+ int dataSize = pPhysModel->dataSize + pPhysModel->keydataSize;
+ Q_memcpy( pSrcAligned, pSrc, pPhysModel->keydataSize );
+ pSrc += pPhysModel->keydataSize;
+ pSrcAligned += pPhysModel->keydataSize;
+ if ( dataSize % 4 != 0 )
+ {
+ // Next chunk will be unaligned
+ padBytes = ( 4 - dataSize % 4 );
+ pPhysModel->keydataSize += padBytes;
+ count += padBytes;
+ Q_memset( pSrcAligned, 0, padBytes );
+ pSrcAligned += padBytes;
+ }
+ }
+ } while ( pPhysModel->dataSize > 0 );
+
+ // Now the data can be swapped properly
+ pSrcBase = pSrcAlignedBase;
+ pSrc = pSrcBase;
+ byte *pDest = pDestBase;
+
+ do
+ {
+ // src headers are in native format
+ pPhysModel = (dphysmodel_t*)pSrc;
+ if ( g_bSwapOnWrite )
+ {
+ g_Swap.SwapFieldsToTargetEndian( (dphysmodel_t*)pDest, (dphysmodel_t*)pSrc );
+ }
+ else
+ {
+ Q_memcpy( pDest, pSrc, sizeof(dphysmodel_t) );
+ }
+
+ pSrc += sizeof(dphysmodel_t);
+ pDest += sizeof(dphysmodel_t);
+
+ pSrcBase = pSrc;
+ pDestBase = pDest;
+
+ if ( pPhysModel->dataSize > 0 )
+ {
+ vcollide_t collide = {0};
+ int dataSize = pPhysModel->dataSize + pPhysModel->keydataSize;
+
+ if ( g_bSwapOnWrite )
+ {
+ // Load the collide data
+ physcollision->VCollideLoad( &collide, pPhysModel->solidCount, (const char *)pSrc, dataSize, false );
+ }
+
+ int *offsets = new int[ pPhysModel->solidCount ];
+
+ // Swap the collision data headers
+ for ( int i = 0; i < pPhysModel->solidCount; ++i )
+ {
+ int headerSize = 0;
+ swapcollideheader_t *baseHdr = (swapcollideheader_t*)pSrc;
+ short modelType = baseHdr->modelType;
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapBufferToTargetEndian( &modelType );
+ }
+
+ if ( modelType == 0 ) // COLLIDE_POLY
+ {
+ headerSize = sizeof(swapcompactsurfaceheader_t);
+ swapcompactsurfaceheader_t swapHdr;
+ Q_memcpy( &swapHdr, pSrc, headerSize );
+ g_Swap.SwapFieldsToTargetEndian( &swapHdr, &swapHdr );
+ Q_memcpy( pDest, &swapHdr, headerSize );
+ }
+ else if ( modelType == 1 ) // COLLIDE_MOPP
+ {
+ // The PC still unserializes these, but we don't support them
+ if ( g_bSwapOnWrite )
+ {
+ collide.solids[i] = NULL;
+ }
+
+ headerSize = sizeof(swapmoppsurfaceheader_t);
+ swapmoppsurfaceheader_t swapHdr;
+ Q_memcpy( &swapHdr, pSrc, headerSize );
+ g_Swap.SwapFieldsToTargetEndian( &swapHdr, &swapHdr );
+ Q_memcpy( pDest, &swapHdr, headerSize );
+
+ }
+ else
+ {
+ // Shouldn't happen
+ Assert( 0 );
+ }
+
+ if ( g_bSwapOnLoad )
+ {
+ // src needs the native header data to load the vcollides
+ Q_memcpy( pSrc, pDest, headerSize );
+ }
+ // HACK: Need either surfaceSize or moppSize - both sit at the same offset in the structure
+ swapmoppsurfaceheader_t *hdr = (swapmoppsurfaceheader_t*)pSrc;
+ pSrc += hdr->size + sizeof(int);
+ pDest += hdr->size + sizeof(int);
+ offsets[i] = hdr->size;
+ }
+
+ pSrc = pSrcBase;
+ pDest = pDestBase;
+ if ( g_bSwapOnLoad )
+ {
+ physcollision->VCollideLoad( &collide, pPhysModel->solidCount, (const char *)pSrc, dataSize, true );
+ }
+
+ // Write out the ledge tree data
+ for ( int i = 0; i < pPhysModel->solidCount; ++i )
+ {
+ if ( collide.solids[i] )
+ {
+ // skip over the size member
+ pSrc += sizeof(int);
+ pDest += sizeof(int);
+ int offset = physcollision->CollideWrite( (char*)pDest, collide.solids[i], g_bSwapOnWrite );
+ pSrc += offset;
+ pDest += offset;
+ }
+ else
+ {
+ pSrc += offsets[i] + sizeof(int);
+ pDest += offsets[i] + sizeof(int);
+ }
+ }
+
+ // copy the keyvalues data
+ Q_memcpy( pDest, pSrc, pPhysModel->keydataSize );
+ pDest += pPhysModel->keydataSize;
+ pSrc += pPhysModel->keydataSize;
+
+ // Free the memory
+ physcollision->VCollideUnload( &collide );
+ delete [] offsets;
+ }
+
+ // avoid infinite loop on badly formed file
+ if ( (pSrc - basePtr) > count )
+ break;
+
+ } while ( pPhysModel->dataSize > 0 );
+
+ free( pSrcAlignedBase );
+}
+
+
+// UNDONE: This code is not yet tested.
+static void SwapPhysdispLump( byte *pDest, byte *pSrc, int count )
+{
+ // the format of this lump is one unsigned short dispCount, then dispCount unsigned shorts of sizes
+ // followed by an array of variable length (each element is the length of the corresponding entry in the
+ // previous table) byte-stream data structure of the displacement collision models
+ // these byte-stream structs are endian-neutral because each element is byte-sized
+ unsigned short dispCount = *(unsigned short*)pSrc;
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapBufferToTargetEndian( &dispCount );
+ }
+ g_Swap.SwapBufferToTargetEndian( (unsigned short*)pDest, (unsigned short*)pSrc, dispCount + 1 );
+
+ const int nBytes = (dispCount + 1) * sizeof( unsigned short );
+ pSrc += nBytes;
+ pDest += nBytes;
+ count -= nBytes;
+
+ g_Swap.SwapBufferToTargetEndian( pDest, pSrc, count );
+}
+
+
+static void SwapVisibilityLump( byte *pDest, byte *pSrc, int count )
+{
+ int firstInt = *(int*)pSrc;
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapBufferToTargetEndian( &firstInt );
+ }
+ int intCt = firstInt * 2 + 1;
+ const int hdrSize = intCt * sizeof(int);
+ g_Swap.SwapBufferToTargetEndian( (int*)pDest, (int*)pSrc, intCt );
+ g_Swap.SwapBufferToTargetEndian( pDest + hdrSize, pSrc + hdrSize, count - hdrSize );
+}
+
+//=============================================================================
+void Lumps_Init( void )
+{
+ memset( &g_Lumps, 0, sizeof(g_Lumps) );
+}
+
+int LumpVersion( int lump )
+{
+ return g_pBSPHeader->lumps[lump].version;
+}
+
+bool HasLump( int lump )
+{
+ return g_pBSPHeader->lumps[lump].filelen > 0;
+}
+
+void ValidateLump( int lump, int length, int size, int forceVersion )
+{
+ if ( length % size )
+ {
+ Error( "ValidateLump: odd size for lump %d", lump );
+ }
+
+ if ( forceVersion >= 0 && forceVersion != g_pBSPHeader->lumps[lump].version )
+ {
+ Error( "ValidateLump: old version for lump %d in map!", lump );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Add Lumps of integral types without datadescs
+//-----------------------------------------------------------------------------
+template< class T >
+int CopyLumpInternal( int fieldType, int lump, T *dest, int forceVersion )
+{
+ g_Lumps.bLumpParsed[lump] = true;
+
+ // Vectors are passed in as floats
+ int fieldSize = ( fieldType == FIELD_VECTOR ) ? sizeof(Vector) : sizeof(T);
+ unsigned int length = g_pBSPHeader->lumps[lump].filelen;
+ unsigned int ofs = g_pBSPHeader->lumps[lump].fileofs;
+
+ // count must be of the integral type
+ unsigned int count = length / sizeof(T);
+
+ ValidateLump( lump, length, fieldSize, forceVersion );
+
+ if ( g_bSwapOnLoad )
+ {
+ switch( lump )
+ {
+ case LUMP_VISIBILITY:
+ SwapVisibilityLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count );
+ break;
+
+ case LUMP_PHYSCOLLIDE:
+ // SwapPhyscollideLump may change size
+ SwapPhyscollideLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count );
+ length = count;
+ break;
+
+ case LUMP_PHYSDISP:
+ SwapPhysdispLump( (byte*)dest, ((byte*)g_pBSPHeader + ofs), count );
+ break;
+
+ default:
+ g_Swap.SwapBufferToTargetEndian( dest, (T*)((byte*)g_pBSPHeader + ofs), count );
+ break;
+ }
+ }
+ else
+ {
+ memcpy( dest, (byte*)g_pBSPHeader + ofs, length );
+ }
+
+ // Return actual count of elements
+ return length / fieldSize;
+}
+
+template< class T >
+int CopyLump( int fieldType, int lump, T *dest, int forceVersion = -1 )
+{
+ return CopyLumpInternal( fieldType, lump, dest, forceVersion );
+}
+
+template< class T >
+void CopyLump( int fieldType, int lump, CUtlVector<T> &dest, int forceVersion = -1 )
+{
+ Assert( fieldType != FIELD_VECTOR ); // TODO: Support this if necessary
+ dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) );
+ CopyLumpInternal( fieldType, lump, dest.Base(), forceVersion );
+}
+
+template< class T >
+void CopyOptionalLump( int fieldType, int lump, CUtlVector<T> &dest, int forceVersion = -1 )
+{
+ // not fatal if not present
+ if ( !HasLump( lump ) )
+ return;
+
+ dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) );
+ CopyLumpInternal( fieldType, lump, dest.Base(), forceVersion );
+}
+
+template< class T >
+int CopyVariableLump( int fieldType, int lump, void **dest, int forceVersion = -1 )
+{
+ int length = g_pBSPHeader->lumps[lump].filelen;
+ *dest = malloc( length );
+
+ return CopyLumpInternal<T>( fieldType, lump, (T*)*dest, forceVersion );
+}
+
+//-----------------------------------------------------------------------------
+// Add Lumps of object types with datadescs
+//-----------------------------------------------------------------------------
+template< class T >
+int CopyLumpInternal( int lump, T *dest, int forceVersion )
+{
+ g_Lumps.bLumpParsed[lump] = true;
+
+ unsigned int length = g_pBSPHeader->lumps[lump].filelen;
+ unsigned int ofs = g_pBSPHeader->lumps[lump].fileofs;
+ unsigned int count = length / sizeof(T);
+
+ ValidateLump( lump, length, sizeof(T), forceVersion );
+
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapFieldsToTargetEndian( dest, (T*)((byte*)g_pBSPHeader + ofs), count );
+ }
+ else
+ {
+ memcpy( dest, (byte*)g_pBSPHeader + ofs, length );
+ }
+
+ return count;
+}
+
+template< class T >
+int CopyLump( int lump, T *dest, int forceVersion = -1 )
+{
+ return CopyLumpInternal( lump, dest, forceVersion );
+}
+
+template< class T >
+void CopyLump( int lump, CUtlVector<T> &dest, int forceVersion = -1 )
+{
+ dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) );
+ CopyLumpInternal( lump, dest.Base(), forceVersion );
+}
+
+template< class T >
+void CopyOptionalLump( int lump, CUtlVector<T> &dest, int forceVersion = -1 )
+{
+ // not fatal if not present
+ if ( !HasLump( lump ) )
+ return;
+
+ dest.SetSize( g_pBSPHeader->lumps[lump].filelen / sizeof(T) );
+ CopyLumpInternal( lump, dest.Base(), forceVersion );
+}
+
+template< class T >
+int CopyVariableLump( int lump, void **dest, int forceVersion = -1 )
+{
+ int length = g_pBSPHeader->lumps[lump].filelen;
+ *dest = malloc( length );
+
+ return CopyLumpInternal<T>( lump, (T*)*dest, forceVersion );
+}
+
+//-----------------------------------------------------------------------------
+// Add/Write unknown lumps
+//-----------------------------------------------------------------------------
+void Lumps_Parse( void )
+{
+ int i;
+
+ for ( i = 0; i < HEADER_LUMPS; i++ )
+ {
+ if ( !g_Lumps.bLumpParsed[i] && g_pBSPHeader->lumps[i].filelen )
+ {
+ g_Lumps.size[i] = CopyVariableLump<byte>( FIELD_CHARACTER, i, &g_Lumps.pLumps[i], -1 );
+ Msg( "Reading unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] );
+ }
+ }
+}
+
+void Lumps_Write( void )
+{
+ int i;
+
+ for ( i = 0; i < HEADER_LUMPS; i++ )
+ {
+ if ( g_Lumps.size[i] )
+ {
+ Msg( "Writing unknown lump #%d (%d bytes)\n", i, g_Lumps.size[i] );
+ AddLump( i, (byte*)g_Lumps.pLumps[i], g_Lumps.size[i] );
+ }
+ if ( g_Lumps.pLumps[i] )
+ {
+ free( g_Lumps.pLumps[i] );
+ g_Lumps.pLumps[i] = NULL;
+ }
+ }
+}
+
+int LoadLeafs( void )
+{
+#if defined( BSP_USE_LESS_MEMORY )
+ dleafs = (dleaf_t*)malloc( g_pBSPHeader->lumps[LUMP_LEAFS].filelen );
+#endif
+
+ switch ( LumpVersion( LUMP_LEAFS ) )
+ {
+ case 0:
+ {
+ g_Lumps.bLumpParsed[LUMP_LEAFS] = true;
+ int length = g_pBSPHeader->lumps[LUMP_LEAFS].filelen;
+ int size = sizeof( dleaf_version_0_t );
+ if ( length % size )
+ {
+ Error( "odd size for LUMP_LEAFS\n" );
+ }
+ int count = length / size;
+
+ void *pSrcBase = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAFS].fileofs );
+ dleaf_version_0_t *pSrc = (dleaf_version_0_t *)pSrcBase;
+ dleaf_t *pDst = dleafs;
+
+ // version 0 predates HDR, build the LDR
+ g_LeafAmbientLightingLDR.SetCount( count );
+ g_LeafAmbientIndexLDR.SetCount( count );
+
+ dleafambientlighting_t *pDstLeafAmbientLighting = &g_LeafAmbientLightingLDR[0];
+ for ( int i = 0; i < count; i++ )
+ {
+ g_LeafAmbientIndexLDR[i].ambientSampleCount = 1;
+ g_LeafAmbientIndexLDR[i].firstAmbientSample = i;
+
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapFieldsToTargetEndian( pSrc );
+ }
+ // pDst is a subset of pSrc;
+ *pDst = *( ( dleaf_t * )( void * )pSrc );
+ pDstLeafAmbientLighting->cube = pSrc->m_AmbientLighting;
+ pDstLeafAmbientLighting->x = pDstLeafAmbientLighting->y = pDstLeafAmbientLighting->z = pDstLeafAmbientLighting->pad = 0;
+ pDst++;
+ pSrc++;
+ pDstLeafAmbientLighting++;
+ }
+ return count;
+ }
+
+ case 1:
+ return CopyLump( LUMP_LEAFS, dleafs );
+
+ default:
+ Assert( 0 );
+ Error( "Unknown LUMP_LEAFS version\n" );
+ return 0;
+ }
+}
+
+void LoadLeafAmbientLighting( int numLeafs )
+{
+ if ( LumpVersion( LUMP_LEAFS ) == 0 )
+ {
+ // an older leaf version already built the LDR ambient lighting on load
+ return;
+ }
+
+ // old BSP with ambient, or new BSP with no lighting, convert ambient light to new format or create dummy ambient
+ if ( !HasLump( LUMP_LEAF_AMBIENT_INDEX ) )
+ {
+ // a bunch of legacy maps, have these lumps with garbage versions
+ // expect them to be NOT the current version
+ if ( HasLump(LUMP_LEAF_AMBIENT_LIGHTING) )
+ {
+ Assert( LumpVersion( LUMP_LEAF_AMBIENT_LIGHTING ) != LUMP_LEAF_AMBIENT_LIGHTING_VERSION );
+ }
+ if ( HasLump(LUMP_LEAF_AMBIENT_LIGHTING_HDR) )
+ {
+ Assert( LumpVersion( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) != LUMP_LEAF_AMBIENT_LIGHTING_VERSION );
+ }
+
+ void *pSrcBase = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].fileofs );
+ CompressedLightCube *pSrc = NULL;
+ if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING ) )
+ {
+ pSrc = (CompressedLightCube*)pSrcBase;
+ }
+ g_LeafAmbientIndexLDR.SetCount( numLeafs );
+ g_LeafAmbientLightingLDR.SetCount( numLeafs );
+
+ void *pSrcBaseHDR = ( ( byte * )g_pBSPHeader + g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].fileofs );
+ CompressedLightCube *pSrcHDR = NULL;
+ if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) )
+ {
+ pSrcHDR = (CompressedLightCube*)pSrcBaseHDR;
+ }
+ g_LeafAmbientIndexHDR.SetCount( numLeafs );
+ g_LeafAmbientLightingHDR.SetCount( numLeafs );
+
+ for ( int i = 0; i < numLeafs; i++ )
+ {
+ g_LeafAmbientIndexLDR[i].ambientSampleCount = 1;
+ g_LeafAmbientIndexLDR[i].firstAmbientSample = i;
+ g_LeafAmbientIndexHDR[i].ambientSampleCount = 1;
+ g_LeafAmbientIndexHDR[i].firstAmbientSample = i;
+
+ Q_memset( &g_LeafAmbientLightingLDR[i], 0, sizeof(g_LeafAmbientLightingLDR[i]) );
+ Q_memset( &g_LeafAmbientLightingHDR[i], 0, sizeof(g_LeafAmbientLightingHDR[i]) );
+
+ if ( pSrc )
+ {
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapFieldsToTargetEndian( &pSrc[i] );
+ }
+ g_LeafAmbientLightingLDR[i].cube = pSrc[i];
+ }
+ if ( pSrcHDR )
+ {
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.SwapFieldsToTargetEndian( &pSrcHDR[i] );
+ }
+ g_LeafAmbientLightingHDR[i].cube = pSrcHDR[i];
+ }
+ }
+
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING] = true;
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX] = true;
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING_HDR] = true;
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX_HDR] = true;
+ }
+ else
+ {
+ CopyOptionalLump( LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR );
+ CopyOptionalLump( LUMP_LEAF_AMBIENT_INDEX, g_LeafAmbientIndexLDR );
+ CopyOptionalLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR );
+ CopyOptionalLump( LUMP_LEAF_AMBIENT_INDEX_HDR, g_LeafAmbientIndexHDR );
+ }
+}
+
+void ValidateHeader( const char *filename, const dheader_t *pHeader )
+{
+ if ( pHeader->ident != IDBSPHEADER )
+ {
+ Error ("%s is not a IBSP file", filename);
+ }
+ if ( pHeader->version < MINBSPVERSION || pHeader->version > BSPVERSION )
+ {
+ Error ("%s is version %i, not %i", filename, pHeader->version, BSPVERSION);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Low level BSP opener for external parsing. Parses headers, but nothing else.
+// You must close the BSP, via CloseBSPFile().
+//-----------------------------------------------------------------------------
+void OpenBSPFile( const char *filename )
+{
+ Lumps_Init();
+
+ // load the file header
+ LoadFile( filename, (void **)&g_pBSPHeader );
+
+ if ( g_bSwapOnLoad )
+ {
+ g_Swap.ActivateByteSwapping( true );
+ g_Swap.SwapFieldsToTargetEndian( g_pBSPHeader );
+ }
+
+ ValidateHeader( filename, g_pBSPHeader );
+
+ g_MapRevision = g_pBSPHeader->mapRevision;
+}
+
+//-----------------------------------------------------------------------------
+// CloseBSPFile
+//-----------------------------------------------------------------------------
+void CloseBSPFile( void )
+{
+ free( g_pBSPHeader );
+ g_pBSPHeader = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// LoadBSPFile
+//-----------------------------------------------------------------------------
+void LoadBSPFile( const char *filename )
+{
+ OpenBSPFile( filename );
+
+ nummodels = CopyLump( LUMP_MODELS, dmodels );
+ numvertexes = CopyLump( LUMP_VERTEXES, dvertexes );
+ numplanes = CopyLump( LUMP_PLANES, dplanes );
+ numleafs = LoadLeafs();
+ numnodes = CopyLump( LUMP_NODES, dnodes );
+ CopyLump( LUMP_TEXINFO, texinfo );
+ numtexdata = CopyLump( LUMP_TEXDATA, dtexdata );
+
+ CopyLump( LUMP_DISPINFO, g_dispinfo );
+ CopyLump( LUMP_DISP_VERTS, g_DispVerts );
+ CopyLump( LUMP_DISP_TRIS, g_DispTris );
+ CopyLump( FIELD_CHARACTER, LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions );
+ CopyLump( LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos );
+
+ numfaces = CopyLump(LUMP_FACES, dfaces, LUMP_FACES_VERSION);
+ if ( HasLump( LUMP_FACES_HDR ) )
+ numfaces_hdr = CopyLump( LUMP_FACES_HDR, dfaces_hdr, LUMP_FACES_VERSION );
+ else
+ numfaces_hdr = 0;
+
+ CopyOptionalLump( LUMP_FACEIDS, dfaceids );
+
+ g_numprimitives = CopyLump( LUMP_PRIMITIVES, g_primitives );
+ g_numprimverts = CopyLump( LUMP_PRIMVERTS, g_primverts );
+ g_numprimindices = CopyLump( FIELD_SHORT, LUMP_PRIMINDICES, g_primindices );
+ numorigfaces = CopyLump( LUMP_ORIGINALFACES, dorigfaces ); // original faces
+ numleaffaces = CopyLump( FIELD_SHORT, LUMP_LEAFFACES, dleaffaces );
+ numleafbrushes = CopyLump( FIELD_SHORT, LUMP_LEAFBRUSHES, dleafbrushes );
+ numsurfedges = CopyLump( FIELD_INTEGER, LUMP_SURFEDGES, dsurfedges );
+ numedges = CopyLump( LUMP_EDGES, dedges );
+ numbrushes = CopyLump( LUMP_BRUSHES, dbrushes );
+ numbrushsides = CopyLump( LUMP_BRUSHSIDES, dbrushsides );
+ numareas = CopyLump( LUMP_AREAS, dareas );
+ numareaportals = CopyLump( LUMP_AREAPORTALS, dareaportals );
+
+ visdatasize = CopyLump ( FIELD_CHARACTER, LUMP_VISIBILITY, dvisdata );
+ CopyOptionalLump( FIELD_CHARACTER, LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION );
+ CopyOptionalLump( FIELD_CHARACTER, LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION );
+
+ LoadLeafAmbientLighting( numleafs );
+
+ CopyLump( FIELD_CHARACTER, LUMP_ENTITIES, dentdata );
+ numworldlightsLDR = CopyLump( LUMP_WORLDLIGHTS, dworldlightsLDR );
+ numworldlightsHDR = CopyLump( LUMP_WORLDLIGHTS_HDR, dworldlightsHDR );
+
+ numleafwaterdata = CopyLump( LUMP_LEAFWATERDATA, dleafwaterdata );
+ g_PhysCollideSize = CopyVariableLump<byte>( FIELD_CHARACTER, LUMP_PHYSCOLLIDE, (void**)&g_pPhysCollide );
+ g_PhysDispSize = CopyVariableLump<byte>( FIELD_CHARACTER, LUMP_PHYSDISP, (void**)&g_pPhysDisp );
+
+ g_numvertnormals = CopyLump( FIELD_VECTOR, LUMP_VERTNORMALS, (float*)g_vertnormals );
+ g_numvertnormalindices = CopyLump( FIELD_SHORT, LUMP_VERTNORMALINDICES, g_vertnormalindices );
+
+ g_nClipPortalVerts = CopyLump( FIELD_VECTOR, LUMP_CLIPPORTALVERTS, (float*)g_ClipPortalVerts );
+ g_nCubemapSamples = CopyLump( LUMP_CUBEMAPS, g_CubemapSamples );
+
+ CopyLump( FIELD_CHARACTER, LUMP_TEXDATA_STRING_DATA, g_TexDataStringData );
+ CopyLump( FIELD_INTEGER, LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable );
+
+ g_nOverlayCount = CopyLump( LUMP_OVERLAYS, g_Overlays );
+ g_nWaterOverlayCount = CopyLump( LUMP_WATEROVERLAYS, g_WaterOverlays );
+ CopyLump( LUMP_OVERLAY_FADES, g_OverlayFades );
+
+ dflagslump_t flags_lump;
+
+ if ( HasLump( LUMP_MAP_FLAGS ) )
+ CopyLump ( LUMP_MAP_FLAGS, &flags_lump );
+ else
+ memset( &flags_lump, 0, sizeof( flags_lump ) ); // default flags to 0
+
+ g_LevelFlags = flags_lump.m_LevelFlags;
+
+ LoadOcclusionLump();
+
+ CopyLump( FIELD_SHORT, LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater );
+
+ /*
+ int crap;
+ for( crap = 0; crap < g_nBSPStringTable; crap++ )
+ {
+ Msg( "stringtable %d", ( int )crap );
+ Msg( " %d:", ( int )g_BSPStringTable[crap] );
+ puts( &g_BSPStringData[g_BSPStringTable[crap]] );
+ puts( "\n" );
+ }
+ */
+
+ // Load PAK file lump into appropriate data structure
+ byte *pakbuffer = NULL;
+ int paksize = CopyVariableLump<byte>( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer );
+ if ( paksize > 0 )
+ {
+ GetPakFile()->ActivateByteSwapping( IsX360() );
+ GetPakFile()->ParseFromBuffer( pakbuffer, paksize );
+ }
+ else
+ {
+ GetPakFile()->Reset();
+ }
+
+ free( pakbuffer );
+
+ g_GameLumps.ParseGameLump( g_pBSPHeader );
+
+ // NOTE: Do NOT call CopyLump after Lumps_Parse() it parses all un-Copied lumps
+ // parse any additional lumps
+ Lumps_Parse();
+
+ // everything has been copied out
+ CloseBSPFile();
+
+ g_Swap.ActivateByteSwapping( false );
+}
+
+//-----------------------------------------------------------------------------
+// Reset any state.
+//-----------------------------------------------------------------------------
+void UnloadBSPFile()
+{
+ nummodels = 0;
+ numvertexes = 0;
+ numplanes = 0;
+
+ numleafs = 0;
+#if defined( BSP_USE_LESS_MEMORY )
+ if ( dleafs )
+ {
+ free( dleafs );
+ dleafs = NULL;
+ }
+#endif
+
+ numnodes = 0;
+ texinfo.Purge();
+ numtexdata = 0;
+
+ g_dispinfo.Purge();
+ g_DispVerts.Purge();
+ g_DispTris.Purge();
+
+ g_DispLightmapSamplePositions.Purge();
+ g_FaceMacroTextureInfos.Purge();
+
+ numfaces = 0;
+ numfaces_hdr = 0;
+
+ dfaceids.Purge();
+
+ g_numprimitives = 0;
+ g_numprimverts = 0;
+ g_numprimindices = 0;
+ numorigfaces = 0;
+ numleaffaces = 0;
+ numleafbrushes = 0;
+ numsurfedges = 0;
+ numedges = 0;
+ numbrushes = 0;
+ numbrushsides = 0;
+ numareas = 0;
+ numareaportals = 0;
+
+ visdatasize = 0;
+ dlightdataLDR.Purge();
+ dlightdataHDR.Purge();
+
+ g_LeafAmbientLightingLDR.Purge();
+ g_LeafAmbientLightingHDR.Purge();
+ g_LeafAmbientIndexHDR.Purge();
+ g_LeafAmbientIndexLDR.Purge();
+
+ dentdata.Purge();
+ numworldlightsLDR = 0;
+ numworldlightsHDR = 0;
+
+ numleafwaterdata = 0;
+
+ if ( g_pPhysCollide )
+ {
+ free( g_pPhysCollide );
+ g_pPhysCollide = NULL;
+ }
+ g_PhysCollideSize = 0;
+
+ if ( g_pPhysDisp )
+ {
+ free( g_pPhysDisp );
+ g_pPhysDisp = NULL;
+ }
+ g_PhysDispSize = 0;
+
+ g_numvertnormals = 0;
+ g_numvertnormalindices = 0;
+
+ g_nClipPortalVerts = 0;
+ g_nCubemapSamples = 0;
+
+ g_TexDataStringData.Purge();
+ g_TexDataStringTable.Purge();
+
+ g_nOverlayCount = 0;
+ g_nWaterOverlayCount = 0;
+
+ g_LevelFlags = 0;
+
+ g_OccluderData.Purge();
+ g_OccluderPolyData.Purge();
+ g_OccluderVertexIndices.Purge();
+
+ g_GameLumps.DestroyAllGameLumps();
+
+ for ( int i = 0; i < HEADER_LUMPS; i++ )
+ {
+ if ( g_Lumps.pLumps[i] )
+ {
+ free( g_Lumps.pLumps[i] );
+ g_Lumps.pLumps[i] = NULL;
+ }
+ }
+
+ ReleasePakFileLumps();
+}
+
+//-----------------------------------------------------------------------------
+// LoadBSPFileFilesystemOnly
+//-----------------------------------------------------------------------------
+void LoadBSPFile_FileSystemOnly( const char *filename )
+{
+ Lumps_Init();
+
+ //
+ // load the file header
+ //
+ LoadFile( filename, (void **)&g_pBSPHeader );
+
+ ValidateHeader( filename, g_pBSPHeader );
+
+ // Load PAK file lump into appropriate data structure
+ byte *pakbuffer = NULL;
+ int paksize = CopyVariableLump<byte>( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer, 1 );
+ if ( paksize > 0 )
+ {
+ GetPakFile()->ParseFromBuffer( pakbuffer, paksize );
+ }
+ else
+ {
+ GetPakFile()->Reset();
+ }
+
+ free( pakbuffer );
+
+ // everything has been copied out
+ free( g_pBSPHeader );
+ g_pBSPHeader = NULL;
+}
+
+void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName )
+{
+ Lumps_Init();
+
+ //
+ // load the file header
+ //
+ LoadFile( pBSPFileName, (void **)&g_pBSPHeader);
+
+ ValidateHeader( pBSPFileName, g_pBSPHeader );
+
+ byte *pakbuffer = NULL;
+ int paksize = CopyVariableLump<byte>( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer );
+ if ( paksize > 0 )
+ {
+ FILE *fp;
+ fp = fopen( pZipFileName, "wb" );
+ if( !fp )
+ {
+ fprintf( stderr, "can't open %s\n", pZipFileName );
+ return;
+ }
+
+ fwrite( pakbuffer, paksize, 1, fp );
+ fclose( fp );
+ }
+ else
+ {
+ fprintf( stderr, "zip file is zero length!\n" );
+ }
+}
+
+/*
+=============
+LoadBSPFileTexinfo
+
+Only loads the texinfo lump, so qdata can scan for textures
+=============
+*/
+void LoadBSPFileTexinfo( const char *filename )
+{
+ FILE *f;
+ int length, ofs;
+
+ g_pBSPHeader = (dheader_t*)malloc( sizeof(dheader_t) );
+
+ f = fopen( filename, "rb" );
+ fread( g_pBSPHeader, sizeof(dheader_t), 1, f);
+
+ ValidateHeader( filename, g_pBSPHeader );
+
+ length = g_pBSPHeader->lumps[LUMP_TEXINFO].filelen;
+ ofs = g_pBSPHeader->lumps[LUMP_TEXINFO].fileofs;
+
+ int nCount = length / sizeof(texinfo_t);
+
+ texinfo.Purge();
+ texinfo.AddMultipleToTail( nCount );
+
+ fseek( f, ofs, SEEK_SET );
+ fread( texinfo.Base(), length, 1, f );
+ fclose( f );
+
+ // everything has been copied out
+ free( g_pBSPHeader );
+ g_pBSPHeader = NULL;
+}
+
+static void AddLumpInternal( int lumpnum, void *data, int len, int version )
+{
+ lump_t *lump;
+
+ g_Lumps.size[lumpnum] = 0; // mark it written
+
+ lump = &g_pBSPHeader->lumps[lumpnum];
+
+ lump->fileofs = g_pFileSystem->Tell( g_hBSPFile );
+ lump->filelen = len;
+ lump->version = version;
+ lump->fourCC[0] = ( char )0;
+ lump->fourCC[1] = ( char )0;
+ lump->fourCC[2] = ( char )0;
+ lump->fourCC[3] = ( char )0;
+
+ SafeWrite( g_hBSPFile, data, len );
+
+ // pad out to the next dword
+ AlignFilePosition( g_hBSPFile, 4 );
+}
+
+template< class T >
+static void SwapInPlace( T *pData, int count )
+{
+ if ( !pData )
+ return;
+
+ // use the datadesc to swap the fields in place
+ g_Swap.SwapFieldsToTargetEndian<T>( (T*)pData, pData, count );
+}
+
+template< class T >
+static void SwapInPlace( int fieldType, T *pData, int count )
+{
+ if ( !pData )
+ return;
+
+ // swap the data in place
+ g_Swap.SwapBufferToTargetEndian<T>( (T*)pData, (T*)pData, count );
+}
+
+//-----------------------------------------------------------------------------
+// Add raw data chunk to file (not a lump)
+//-----------------------------------------------------------------------------
+template< class T >
+static void WriteData( int fieldType, T *pData, int count )
+{
+ if ( g_bSwapOnWrite )
+ {
+ SwapInPlace( fieldType, pData, count );
+ }
+ SafeWrite( g_hBSPFile, pData, count * sizeof(T) );
+}
+
+template< class T >
+static void WriteData( T *pData, int count )
+{
+ if ( g_bSwapOnWrite )
+ {
+ SwapInPlace( pData, count );
+ }
+ SafeWrite( g_hBSPFile, pData, count * sizeof(T) );
+}
+
+//-----------------------------------------------------------------------------
+// Add Lump of object types with datadescs
+//-----------------------------------------------------------------------------
+template< class T >
+static void AddLump( int lumpnum, T *pData, int count, int version )
+{
+ AddLumpInternal( lumpnum, pData, count * sizeof(T), version );
+}
+
+template< class T >
+static void AddLump( int lumpnum, CUtlVector<T> &data, int version )
+{
+ AddLumpInternal( lumpnum, data.Base(), data.Count() * sizeof(T), version );
+}
+
+/*
+=============
+WriteBSPFile
+
+Swaps the bsp file in place, so it should not be referenced again
+=============
+*/
+void WriteBSPFile( const char *filename, char *pUnused )
+{
+ if ( texinfo.Count() > MAX_MAP_TEXINFO )
+ {
+ Error( "Map has too many texinfos (has %d, can have at most %d)\n", texinfo.Count(), MAX_MAP_TEXINFO );
+ return;
+ }
+
+ dheader_t outHeader;
+ g_pBSPHeader = &outHeader;
+ memset( g_pBSPHeader, 0, sizeof( dheader_t ) );
+
+ g_pBSPHeader->ident = IDBSPHEADER;
+ g_pBSPHeader->version = BSPVERSION;
+ g_pBSPHeader->mapRevision = g_MapRevision;
+
+ g_hBSPFile = SafeOpenWrite( filename );
+ WriteData( g_pBSPHeader ); // overwritten later
+
+ AddLump( LUMP_PLANES, dplanes, numplanes );
+ AddLump( LUMP_LEAFS, dleafs, numleafs, LUMP_LEAFS_VERSION );
+ AddLump( LUMP_LEAF_AMBIENT_LIGHTING, g_LeafAmbientLightingLDR, LUMP_LEAF_AMBIENT_LIGHTING_VERSION );
+ AddLump( LUMP_LEAF_AMBIENT_INDEX, g_LeafAmbientIndexLDR );
+ AddLump( LUMP_LEAF_AMBIENT_INDEX_HDR, g_LeafAmbientIndexHDR );
+ AddLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR, g_LeafAmbientLightingHDR, LUMP_LEAF_AMBIENT_LIGHTING_VERSION );
+
+ AddLump( LUMP_VERTEXES, dvertexes, numvertexes );
+ AddLump( LUMP_NODES, dnodes, numnodes );
+ AddLump( LUMP_TEXINFO, texinfo );
+ AddLump( LUMP_TEXDATA, dtexdata, numtexdata );
+
+ AddLump( LUMP_DISPINFO, g_dispinfo );
+ AddLump( LUMP_DISP_VERTS, g_DispVerts );
+ AddLump( LUMP_DISP_TRIS, g_DispTris );
+ AddLump( LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS, g_DispLightmapSamplePositions );
+ AddLump( LUMP_FACE_MACRO_TEXTURE_INFO, g_FaceMacroTextureInfos );
+
+ AddLump( LUMP_PRIMITIVES, g_primitives, g_numprimitives );
+ AddLump( LUMP_PRIMVERTS, g_primverts, g_numprimverts );
+ AddLump( LUMP_PRIMINDICES, g_primindices, g_numprimindices );
+ AddLump( LUMP_FACES, dfaces, numfaces, LUMP_FACES_VERSION );
+ if (numfaces_hdr)
+ AddLump( LUMP_FACES_HDR, dfaces_hdr, numfaces_hdr, LUMP_FACES_VERSION );
+ AddLump ( LUMP_FACEIDS, dfaceids, numfaceids );
+
+ AddLump( LUMP_ORIGINALFACES, dorigfaces, numorigfaces ); // original faces lump
+ AddLump( LUMP_BRUSHES, dbrushes, numbrushes );
+ AddLump( LUMP_BRUSHSIDES, dbrushsides, numbrushsides );
+ AddLump( LUMP_LEAFFACES, dleaffaces, numleaffaces );
+ AddLump( LUMP_LEAFBRUSHES, dleafbrushes, numleafbrushes );
+ AddLump( LUMP_SURFEDGES, dsurfedges, numsurfedges );
+ AddLump( LUMP_EDGES, dedges, numedges );
+ AddLump( LUMP_MODELS, dmodels, nummodels );
+ AddLump( LUMP_AREAS, dareas, numareas );
+ AddLump( LUMP_AREAPORTALS, dareaportals, numareaportals );
+
+ AddLump( LUMP_LIGHTING, dlightdataLDR, LUMP_LIGHTING_VERSION );
+ AddLump( LUMP_LIGHTING_HDR, dlightdataHDR, LUMP_LIGHTING_VERSION );
+ AddLump( LUMP_VISIBILITY, dvisdata, visdatasize );
+ AddLump( LUMP_ENTITIES, dentdata );
+ AddLump( LUMP_WORLDLIGHTS, dworldlightsLDR, numworldlightsLDR );
+ AddLump( LUMP_WORLDLIGHTS_HDR, dworldlightsHDR, numworldlightsHDR );
+ AddLump( LUMP_LEAFWATERDATA, dleafwaterdata, numleafwaterdata );
+
+ AddOcclusionLump();
+
+ dflagslump_t flags_lump;
+ flags_lump.m_LevelFlags = g_LevelFlags;
+ AddLump( LUMP_MAP_FLAGS, &flags_lump, 1 );
+
+ // NOTE: This is just for debugging, so it is disabled in release maps
+#if 0
+ // add the vis portals to the BSP for visualization
+ AddLump( LUMP_PORTALS, dportals, numportals );
+ AddLump( LUMP_CLUSTERS, dclusters, numclusters );
+ AddLump( LUMP_PORTALVERTS, dportalverts, numportalverts );
+ AddLump( LUMP_CLUSTERPORTALS, dclusterportals, numclusterportals );
+#endif
+
+ AddLump( LUMP_CLIPPORTALVERTS, (float*)g_ClipPortalVerts, g_nClipPortalVerts * 3 );
+ AddLump( LUMP_CUBEMAPS, g_CubemapSamples, g_nCubemapSamples );
+ AddLump( LUMP_TEXDATA_STRING_DATA, g_TexDataStringData );
+ AddLump( LUMP_TEXDATA_STRING_TABLE, g_TexDataStringTable );
+ AddLump( LUMP_OVERLAYS, g_Overlays, g_nOverlayCount );
+ AddLump( LUMP_WATEROVERLAYS, g_WaterOverlays, g_nWaterOverlayCount );
+ AddLump( LUMP_OVERLAY_FADES, g_OverlayFades, g_nOverlayCount );
+
+ if ( g_pPhysCollide )
+ {
+ AddLump( LUMP_PHYSCOLLIDE, g_pPhysCollide, g_PhysCollideSize );
+ }
+
+ if ( g_pPhysDisp )
+ {
+ AddLump ( LUMP_PHYSDISP, g_pPhysDisp, g_PhysDispSize );
+ }
+
+ AddLump( LUMP_VERTNORMALS, (float*)g_vertnormals, g_numvertnormals * 3 );
+ AddLump( LUMP_VERTNORMALINDICES, g_vertnormalindices, g_numvertnormalindices );
+
+ AddLump( LUMP_LEAFMINDISTTOWATER, g_LeafMinDistToWater, numleafs );
+
+ AddGameLumps();
+
+ // Write pakfile lump to disk
+ WritePakFileLump();
+
+ // NOTE: Do NOT call AddLump after Lumps_Write() it writes all un-Added lumps
+ // write any additional lumps
+ Lumps_Write();
+
+ g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD );
+ WriteData( g_pBSPHeader );
+ g_pFileSystem->Close( g_hBSPFile );
+}
+
+// Generate the next clear lump filename for the bsp file
+bool GenerateNextLumpFileName( const char *bspfilename, char *lumpfilename, int buffsize )
+{
+ for (int i = 0; i < MAX_LUMPFILES; i++)
+ {
+ GenerateLumpFileName( bspfilename, lumpfilename, buffsize, i );
+
+ if ( !g_pFileSystem->FileExists( lumpfilename ) )
+ return true;
+ }
+
+ return false;
+}
+
+void WriteLumpToFile( char *filename, int lump )
+{
+ if ( !HasLump(lump) )
+ return;
+
+ char lumppre[MAX_PATH];
+ if ( !GenerateNextLumpFileName( filename, lumppre, MAX_PATH ) )
+ {
+ Warning( "Failed to find valid lump filename for bsp %s.\n", filename );
+ return;
+ }
+
+ // Open the file
+ FileHandle_t lumpfile = g_pFileSystem->Open(lumppre, "wb");
+ if ( !lumpfile )
+ {
+ Error ("Error opening %s! (Check for write enable)\n",filename);
+ return;
+ }
+
+ int ofs = g_pBSPHeader->lumps[lump].fileofs;
+ int length = g_pBSPHeader->lumps[lump].filelen;
+
+ // Write the header
+ lumpfileheader_t lumpHeader;
+ lumpHeader.lumpID = lump;
+ lumpHeader.lumpVersion = LumpVersion(lump);
+ lumpHeader.lumpLength = length;
+ lumpHeader.mapRevision = LittleLong( g_MapRevision );
+ lumpHeader.lumpOffset = sizeof(lumpfileheader_t); // Lump starts after the header
+ SafeWrite (lumpfile, &lumpHeader, sizeof(lumpfileheader_t));
+
+ // Write the lump
+ SafeWrite (lumpfile, (byte *)g_pBSPHeader + ofs, length);
+}
+
+void WriteLumpToFile( char *filename, int lump, int nLumpVersion, void *pBuffer, size_t nBufLen )
+{
+ char lumppre[MAX_PATH];
+ if ( !GenerateNextLumpFileName( filename, lumppre, MAX_PATH ) )
+ {
+ Warning( "Failed to find valid lump filename for bsp %s.\n", filename );
+ return;
+ }
+
+ // Open the file
+ FileHandle_t lumpfile = g_pFileSystem->Open(lumppre, "wb");
+ if ( !lumpfile )
+ {
+ Error ("Error opening %s! (Check for write enable)\n",filename);
+ return;
+ }
+
+ // Write the header
+ lumpfileheader_t lumpHeader;
+ lumpHeader.lumpID = lump;
+ lumpHeader.lumpVersion = nLumpVersion;
+ lumpHeader.lumpLength = nBufLen;
+ lumpHeader.mapRevision = LittleLong( g_MapRevision );
+ lumpHeader.lumpOffset = sizeof(lumpfileheader_t); // Lump starts after the header
+ SafeWrite( lumpfile, &lumpHeader, sizeof(lumpfileheader_t));
+
+ // Write the lump
+ SafeWrite( lumpfile, pBuffer, nBufLen );
+
+ g_pFileSystem->Close( lumpfile );
+}
+
+
+//============================================================================
+#define ENTRIES(a) (sizeof(a)/sizeof(*(a)))
+#define ENTRYSIZE(a) (sizeof(*(a)))
+
+int ArrayUsage( const char *szItem, int items, int maxitems, int itemsize )
+{
+ float percentage = maxitems ? items * 100.0 / maxitems : 0.0;
+
+ Msg("%-17.17s %8i/%-8i %8i/%-8i (%4.1f%%) ",
+ szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage );
+ if ( percentage > 80.0 )
+ Msg( "VERY FULL!\n" );
+ else if ( percentage > 95.0 )
+ Msg( "SIZE DANGER!\n" );
+ else if ( percentage > 99.9 )
+ Msg( "SIZE OVERFLOW!!!\n" );
+ else
+ Msg( "\n" );
+ return items * itemsize;
+}
+
+int GlobUsage( const char *szItem, int itemstorage, int maxstorage )
+{
+ float percentage = maxstorage ? itemstorage * 100.0 / maxstorage : 0.0;
+ Msg("%-17.17s [variable] %8i/%-8i (%4.1f%%) ",
+ szItem, itemstorage, maxstorage, percentage );
+ if ( percentage > 80.0 )
+ Msg( "VERY FULL!\n" );
+ else if ( percentage > 95.0 )
+ Msg( "SIZE DANGER!\n" );
+ else if ( percentage > 99.9 )
+ Msg( "SIZE OVERFLOW!!!\n" );
+ else
+ Msg( "\n" );
+ return itemstorage;
+}
+
+/*
+=============
+PrintBSPFileSizes
+
+Dumps info about current file
+=============
+*/
+void PrintBSPFileSizes (void)
+{
+ int totalmemory = 0;
+
+// if (!num_entities)
+// ParseEntities ();
+
+ Msg("\n");
+ Msg( "%-17s %16s %16s %9s \n", "Object names", "Objects/Maxobjs", "Memory / Maxmem", "Fullness" );
+ Msg( "%-17s %16s %16s %9s \n", "------------", "---------------", "---------------", "--------" );
+
+ totalmemory += ArrayUsage( "models", nummodels, ENTRIES(dmodels), ENTRYSIZE(dmodels) );
+ totalmemory += ArrayUsage( "brushes", numbrushes, ENTRIES(dbrushes), ENTRYSIZE(dbrushes) );
+ totalmemory += ArrayUsage( "brushsides", numbrushsides, ENTRIES(dbrushsides), ENTRYSIZE(dbrushsides) );
+ totalmemory += ArrayUsage( "planes", numplanes, ENTRIES(dplanes), ENTRYSIZE(dplanes) );
+ totalmemory += ArrayUsage( "vertexes", numvertexes, ENTRIES(dvertexes), ENTRYSIZE(dvertexes) );
+ totalmemory += ArrayUsage( "nodes", numnodes, ENTRIES(dnodes), ENTRYSIZE(dnodes) );
+ totalmemory += ArrayUsage( "texinfos", texinfo.Count(),MAX_MAP_TEXINFO, sizeof(texinfo_t) );
+ totalmemory += ArrayUsage( "texdata", numtexdata, ENTRIES(dtexdata), ENTRYSIZE(dtexdata) );
+
+ totalmemory += ArrayUsage( "dispinfos", g_dispinfo.Count(), 0, sizeof( ddispinfo_t ) );
+ totalmemory += ArrayUsage( "disp_verts", g_DispVerts.Count(), 0, sizeof( g_DispVerts[0] ) );
+ totalmemory += ArrayUsage( "disp_tris", g_DispTris.Count(), 0, sizeof( g_DispTris[0] ) );
+ totalmemory += ArrayUsage( "disp_lmsamples",g_DispLightmapSamplePositions.Count(),0,sizeof( g_DispLightmapSamplePositions[0] ) );
+
+ totalmemory += ArrayUsage( "faces", numfaces, ENTRIES(dfaces), ENTRYSIZE(dfaces) );
+ totalmemory += ArrayUsage( "hdr faces", numfaces_hdr, ENTRIES(dfaces_hdr), ENTRYSIZE(dfaces_hdr) );
+ totalmemory += ArrayUsage( "origfaces", numorigfaces, ENTRIES(dorigfaces), ENTRYSIZE(dorigfaces) ); // original faces
+ totalmemory += ArrayUsage( "leaves", numleafs, ENTRIES(dleafs), ENTRYSIZE(dleafs) );
+ totalmemory += ArrayUsage( "leaffaces", numleaffaces, ENTRIES(dleaffaces), ENTRYSIZE(dleaffaces) );
+ totalmemory += ArrayUsage( "leafbrushes", numleafbrushes, ENTRIES(dleafbrushes), ENTRYSIZE(dleafbrushes) );
+ totalmemory += ArrayUsage( "areas", numareas, ENTRIES(dareas), ENTRYSIZE(dareas) );
+ totalmemory += ArrayUsage( "surfedges", numsurfedges, ENTRIES(dsurfedges), ENTRYSIZE(dsurfedges) );
+ totalmemory += ArrayUsage( "edges", numedges, ENTRIES(dedges), ENTRYSIZE(dedges) );
+ totalmemory += ArrayUsage( "LDR worldlights", numworldlightsLDR, ENTRIES(dworldlightsLDR), ENTRYSIZE(dworldlightsLDR) );
+ totalmemory += ArrayUsage( "HDR worldlights", numworldlightsHDR, ENTRIES(dworldlightsHDR), ENTRYSIZE(dworldlightsHDR) );
+
+ totalmemory += ArrayUsage( "leafwaterdata", numleafwaterdata,ENTRIES(dleafwaterdata), ENTRYSIZE(dleafwaterdata) );
+ totalmemory += ArrayUsage( "waterstrips", g_numprimitives,ENTRIES(g_primitives), ENTRYSIZE(g_primitives) );
+ totalmemory += ArrayUsage( "waterverts", g_numprimverts, ENTRIES(g_primverts), ENTRYSIZE(g_primverts) );
+ totalmemory += ArrayUsage( "waterindices", g_numprimindices,ENTRIES(g_primindices),ENTRYSIZE(g_primindices) );
+ totalmemory += ArrayUsage( "cubemapsamples", g_nCubemapSamples,ENTRIES(g_CubemapSamples),ENTRYSIZE(g_CubemapSamples) );
+ totalmemory += ArrayUsage( "overlays", g_nOverlayCount, ENTRIES(g_Overlays), ENTRYSIZE(g_Overlays) );
+
+ totalmemory += GlobUsage( "LDR lightdata", dlightdataLDR.Count(), 0 );
+ totalmemory += GlobUsage( "HDR lightdata", dlightdataHDR.Count(), 0 );
+ totalmemory += GlobUsage( "visdata", visdatasize, sizeof(dvisdata) );
+ totalmemory += GlobUsage( "entdata", dentdata.Count(), 384*1024 ); // goal is <384K
+
+ totalmemory += ArrayUsage( "LDR ambient table", g_LeafAmbientIndexLDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientIndexLDR[0] ) );
+ totalmemory += ArrayUsage( "HDR ambient table", g_LeafAmbientIndexHDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientIndexHDR[0] ) );
+ totalmemory += ArrayUsage( "LDR leaf ambient lighting", g_LeafAmbientLightingLDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingLDR[0] ) );
+ totalmemory += ArrayUsage( "HDR leaf ambient lighting", g_LeafAmbientLightingHDR.Count(), MAX_MAP_LEAFS, sizeof( g_LeafAmbientLightingHDR[0] ) );
+
+ totalmemory += ArrayUsage( "occluders", g_OccluderData.Count(), 0, sizeof( g_OccluderData[0] ) );
+ totalmemory += ArrayUsage( "occluder polygons", g_OccluderPolyData.Count(), 0, sizeof( g_OccluderPolyData[0] ) );
+ totalmemory += ArrayUsage( "occluder vert ind",g_OccluderVertexIndices.Count(),0, sizeof( g_OccluderVertexIndices[0] ) );
+
+ GameLumpHandle_t h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS );
+ if (h != g_GameLumps.InvalidGameLump())
+ totalmemory += GlobUsage( "detail props", 1, g_GameLumps.GameLumpSize(h) );
+ h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING );
+ if (h != g_GameLumps.InvalidGameLump())
+ totalmemory += GlobUsage( "dtl prp lght", 1, g_GameLumps.GameLumpSize(h) );
+ h = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROP_LIGHTING_HDR );
+ if (h != g_GameLumps.InvalidGameLump())
+ totalmemory += GlobUsage( "HDR dtl prp lght", 1, g_GameLumps.GameLumpSize(h) );
+ h = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
+ if (h != g_GameLumps.InvalidGameLump())
+ totalmemory += GlobUsage( "static props", 1, g_GameLumps.GameLumpSize(h) );
+
+ totalmemory += GlobUsage( "pakfile", GetPakFile()->EstimateSize(), 0 );
+ // HACKHACK: Set physics limit at 4MB, in reality this is totally dynamic
+ totalmemory += GlobUsage( "physics", g_PhysCollideSize, 4*1024*1024 );
+ totalmemory += GlobUsage( "physics terrain", g_PhysDispSize, 1*1024*1024 );
+
+ Msg( "\nLevel flags = %x\n", g_LevelFlags );
+
+ Msg( "\n" );
+
+ int triangleCount = 0;
+
+ for ( int i = 0; i < numfaces; i++ )
+ {
+ // face tris = numedges - 2
+ triangleCount += dfaces[i].numedges - 2;
+ }
+ Msg("Total triangle count: %d\n", triangleCount );
+
+ // UNDONE:
+ // areaportals, portals, texdata, clusters, worldlights, portalverts
+}
+
+/*
+=============
+PrintBSPPackDirectory
+
+Dumps a list of files stored in the bsp pack.
+=============
+*/
+void PrintBSPPackDirectory( void )
+{
+ GetPakFile()->PrintDirectory();
+}
+
+
+//============================================
+
+int num_entities;
+entity_t entities[MAX_MAP_ENTITIES];
+
+void StripTrailing (char *e)
+{
+ char *s;
+
+ s = e + strlen(e)-1;
+ while (s >= e && *s <= 32)
+ {
+ *s = 0;
+ s--;
+ }
+}
+
+/*
+=================
+ParseEpair
+=================
+*/
+epair_t *ParseEpair (void)
+{
+ epair_t *e;
+
+ e = (epair_t*)malloc (sizeof(epair_t));
+ memset (e, 0, sizeof(epair_t));
+
+ if (strlen(token) >= MAX_KEY-1)
+ Error ("ParseEpar: token too long");
+ e->key = copystring(token);
+
+ GetToken (false);
+ if (strlen(token) >= MAX_VALUE-1)
+ Error ("ParseEpar: token too long");
+ e->value = copystring(token);
+
+ // strip trailing spaces
+ StripTrailing (e->key);
+ StripTrailing (e->value);
+
+ return e;
+}
+
+
+/*
+================
+ParseEntity
+================
+*/
+qboolean ParseEntity (void)
+{
+ epair_t *e;
+ entity_t *mapent;
+
+ if (!GetToken (true))
+ return false;
+
+ if (Q_stricmp (token, "{") )
+ Error ("ParseEntity: { not found");
+
+ if (num_entities == MAX_MAP_ENTITIES)
+ Error ("num_entities == MAX_MAP_ENTITIES");
+
+ mapent = &entities[num_entities];
+ num_entities++;
+
+ do
+ {
+ if (!GetToken (true))
+ Error ("ParseEntity: EOF without closing brace");
+ if (!Q_stricmp (token, "}") )
+ break;
+ e = ParseEpair ();
+ e->next = mapent->epairs;
+ mapent->epairs = e;
+ } while (1);
+
+ return true;
+}
+
+/*
+================
+ParseEntities
+
+Parses the dentdata string into entities
+================
+*/
+void ParseEntities (void)
+{
+ num_entities = 0;
+ ParseFromMemory (dentdata.Base(), dentdata.Count());
+
+ while (ParseEntity ())
+ {
+ }
+}
+
+
+/*
+================
+UnparseEntities
+
+Generates the dentdata string from all the entities
+================
+*/
+void UnparseEntities (void)
+{
+ epair_t *ep;
+ char line[2048];
+ int i;
+ char key[1024], value[1024];
+
+ CUtlBuffer buffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ buffer.EnsureCapacity( 256 * 1024 );
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ ep = entities[i].epairs;
+ if (!ep)
+ continue; // ent got removed
+
+ buffer.PutString( "{\n" );
+
+ for (ep = entities[i].epairs ; ep ; ep=ep->next)
+ {
+ strcpy (key, ep->key);
+ StripTrailing (key);
+ strcpy (value, ep->value);
+ StripTrailing (value);
+
+ sprintf(line, "\"%s\" \"%s\"\n", key, value);
+ buffer.PutString( line );
+ }
+ buffer.PutString("}\n");
+ }
+ int entdatasize = buffer.TellPut()+1;
+
+ dentdata.SetSize( entdatasize );
+ memcpy( dentdata.Base(), buffer.Base(), entdatasize-1 );
+ dentdata[entdatasize-1] = 0;
+}
+
+void PrintEntity (entity_t *ent)
+{
+ epair_t *ep;
+
+ Msg ("------- entity %p -------\n", ent);
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ {
+ Msg ("%s = %s\n", ep->key, ep->value);
+ }
+
+}
+
+void SetKeyValue(entity_t *ent, const char *key, const char *value)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!Q_stricmp (ep->key, key) )
+ {
+ free (ep->value);
+ ep->value = copystring(value);
+ return;
+ }
+ ep = (epair_t*)malloc (sizeof(*ep));
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ ep->key = copystring(key);
+ ep->value = copystring(value);
+}
+
+char *ValueForKey (entity_t *ent, char *key)
+{
+ for (epair_t *ep=ent->epairs ; ep ; ep=ep->next)
+ if (!Q_stricmp (ep->key, key) )
+ return ep->value;
+ return "";
+}
+
+vec_t FloatForKey (entity_t *ent, char *key)
+{
+ char *k = ValueForKey (ent, key);
+ return atof(k);
+}
+
+vec_t FloatForKeyWithDefault (entity_t *ent, char *key, float default_value)
+{
+ for (epair_t *ep=ent->epairs ; ep ; ep=ep->next)
+ if (!Q_stricmp (ep->key, key) )
+ return atof( ep->value );
+ return default_value;
+}
+
+
+
+int IntForKey (entity_t *ent, char *key)
+{
+ char *k = ValueForKey (ent, key);
+ return atol(k);
+}
+
+int IntForKeyWithDefault(entity_t *ent, char *key, int nDefault )
+{
+ char *k = ValueForKey (ent, key);
+ if ( !k[0] )
+ return nDefault;
+ return atol(k);
+}
+
+void GetVectorForKey (entity_t *ent, char *key, Vector& vec)
+{
+
+ char *k = ValueForKey (ent, key);
+// scanf into doubles, then assign, so it is vec_t size independent
+ double v1, v2, v3;
+ v1 = v2 = v3 = 0;
+ sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
+ vec[0] = v1;
+ vec[1] = v2;
+ vec[2] = v3;
+}
+
+void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec)
+{
+ double v1, v2;
+
+ char *k = ValueForKey (ent, key);
+// scanf into doubles, then assign, so it is vec_t size independent
+ v1 = v2 = 0;
+ sscanf (k, "%lf %lf", &v1, &v2);
+ vec[0] = v1;
+ vec[1] = v2;
+}
+
+void GetAnglesForKey (entity_t *ent, char *key, QAngle& angle)
+{
+ char *k;
+ double v1, v2, v3;
+
+ k = ValueForKey (ent, key);
+// scanf into doubles, then assign, so it is vec_t size independent
+ v1 = v2 = v3 = 0;
+ sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
+ angle[0] = v1;
+ angle[1] = v2;
+ angle[2] = v3;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void BuildFaceCalcWindingData( dface_t *pFace, int *points )
+{
+ for( int i = 0; i < pFace->numedges; i++ )
+ {
+ int eIndex = dsurfedges[pFace->firstedge+i];
+ if( eIndex < 0 )
+ {
+ points[i] = dedges[-eIndex].v[1];
+ }
+ else
+ {
+ points[i] = dedges[eIndex].v[0];
+ }
+ }
+}
+
+
+void TriStripToTriList(
+ unsigned short const *pTriStripIndices,
+ int nTriStripIndices,
+ unsigned short **pTriListIndices,
+ int *pnTriListIndices )
+{
+ int nMaxTriListIndices = (nTriStripIndices - 2) * 3;
+ *pTriListIndices = new unsigned short[ nMaxTriListIndices ];
+ *pnTriListIndices = 0;
+
+ for( int i=0; i < nTriStripIndices - 2; i++ )
+ {
+ if( pTriStripIndices[i] == pTriStripIndices[i+1] ||
+ pTriStripIndices[i] == pTriStripIndices[i+2] ||
+ pTriStripIndices[i+1] == pTriStripIndices[i+2] )
+ {
+ }
+ else
+ {
+ // Flip odd numbered tris..
+ if( i & 1 )
+ {
+ (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2];
+ (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1];
+ (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i];
+ }
+ else
+ {
+ (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i];
+ (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+1];
+ (*pTriListIndices)[(*pnTriListIndices)++] = pTriStripIndices[i+2];
+ }
+ }
+ }
+}
+
+
+void CalcTextureCoordsAtPoints(
+ float const texelsPerWorldUnits[2][4],
+ int const subtractOffset[2],
+ Vector const *pPoints,
+ int const nPoints,
+ Vector2D *pCoords )
+{
+ for( int i=0; i < nPoints; i++ )
+ {
+ for( int iCoord=0; iCoord < 2; iCoord++ )
+ {
+ float *pDestCoord = &pCoords[i][iCoord];
+
+ *pDestCoord = 0;
+ for( int iDot=0; iDot < 3; iDot++ )
+ *pDestCoord += pPoints[i][iDot] * texelsPerWorldUnits[iCoord][iDot];
+
+ *pDestCoord += texelsPerWorldUnits[iCoord][3];
+ *pDestCoord -= subtractOffset[iCoord];
+ }
+ }
+}
+
+
+/*
+================
+CalcFaceExtents
+
+Fills in s->texmins[] and s->texsize[]
+================
+*/
+void CalcFaceExtents(dface_t *s, int lightmapTextureMinsInLuxels[2], int lightmapTextureSizeInLuxels[2])
+{
+ vec_t mins[2], maxs[2], val=0;
+ int i,j, e=0;
+ dvertex_t *v=NULL;
+ texinfo_t *tex=NULL;
+
+ mins[0] = mins[1] = 1e24;
+ maxs[0] = maxs[1] = -1e24;
+
+ tex = &texinfo[s->texinfo];
+
+ for (i=0 ; i<s->numedges ; i++)
+ {
+ e = dsurfedges[s->firstedge+i];
+ if (e >= 0)
+ v = dvertexes + dedges[e].v[0];
+ else
+ v = dvertexes + dedges[-e].v[1];
+
+ for (j=0 ; j<2 ; j++)
+ {
+ val = v->point[0] * tex->lightmapVecsLuxelsPerWorldUnits[j][0] +
+ v->point[1] * tex->lightmapVecsLuxelsPerWorldUnits[j][1] +
+ v->point[2] * tex->lightmapVecsLuxelsPerWorldUnits[j][2] +
+ tex->lightmapVecsLuxelsPerWorldUnits[j][3];
+ if (val < mins[j])
+ mins[j] = val;
+ if (val > maxs[j])
+ maxs[j] = val;
+ }
+ }
+
+ int nMaxLightmapDim = (s->dispinfo == -1) ? MAX_LIGHTMAP_DIM_WITHOUT_BORDER : MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER;
+ for (i=0 ; i<2 ; i++)
+ {
+ mins[i] = ( float )floor( mins[i] );
+ maxs[i] = ( float )ceil( maxs[i] );
+
+ lightmapTextureMinsInLuxels[i] = ( int )mins[i];
+ lightmapTextureSizeInLuxels[i] = ( int )( maxs[i] - mins[i] );
+ if( lightmapTextureSizeInLuxels[i] > nMaxLightmapDim + 1 )
+ {
+ Vector point = vec3_origin;
+ for (int j=0 ; j<s->numedges ; j++)
+ {
+ e = dsurfedges[s->firstedge+j];
+ v = (e<0)?dvertexes + dedges[-e].v[1] : dvertexes + dedges[e].v[0];
+ point += v->point;
+ Warning( "Bad surface extents point: %f %f %f\n", v->point.x, v->point.y, v->point.z );
+ }
+ point *= 1.0f/s->numedges;
+ Error( "Bad surface extents - surface is too big to have a lightmap\n\tmaterial %s around point (%.1f %.1f %.1f)\n\t(dimension: %d, %d>%d)\n",
+ TexDataStringTable_GetString( dtexdata[texinfo[s->texinfo].texdata].nameStringTableID ),
+ point.x, point.y, point.z,
+ ( int )i,
+ ( int )lightmapTextureSizeInLuxels[i],
+ ( int )( nMaxLightmapDim + 1 )
+ );
+ }
+ }
+}
+
+
+void UpdateAllFaceLightmapExtents()
+{
+ for( int i=0; i < numfaces; i++ )
+ {
+ dface_t *pFace = &dfaces[i];
+
+ if ( texinfo[pFace->texinfo].flags & (SURF_SKY|SURF_NOLIGHT) )
+ continue; // non-lit texture
+
+ CalcFaceExtents( pFace, pFace->m_LightmapTextureMinsInLuxels, pFace->m_LightmapTextureSizeInLuxels );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Helper class to iterate over leaves, used by tools
+//
+//-----------------------------------------------------------------------------
+
+#define TEST_EPSILON (0.03125)
+
+
+class CToolBSPTree : public ISpatialQuery
+{
+public:
+ // Returns the number of leaves
+ int LeafCount() const;
+
+ // Enumerates the leaves along a ray, box, etc.
+ bool EnumerateLeavesAtPoint( Vector const& pt, ISpatialLeafEnumerator* pEnum, int context );
+ bool EnumerateLeavesInBox( Vector const& mins, Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context );
+ bool EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context );
+ bool EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context );
+};
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of leaves
+//-----------------------------------------------------------------------------
+
+int CToolBSPTree::LeafCount() const
+{
+ return numleafs;
+}
+
+
+//-----------------------------------------------------------------------------
+// Enumerates the leaves at a point
+//-----------------------------------------------------------------------------
+
+bool CToolBSPTree::EnumerateLeavesAtPoint( Vector const& pt,
+ ISpatialLeafEnumerator* pEnum, int context )
+{
+ int node = 0;
+ while( node >= 0 )
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ if (DotProduct( pPlane->normal, pt ) <= pPlane->dist)
+ {
+ node = pNode->children[1];
+ }
+ else
+ {
+ node = pNode->children[0];
+ }
+ }
+
+ return pEnum->EnumerateLeaf( - node - 1, context );
+}
+
+
+//-----------------------------------------------------------------------------
+// Enumerates the leaves in a box
+//-----------------------------------------------------------------------------
+
+static bool EnumerateLeavesInBox_R( int node, Vector const& mins,
+ Vector const& maxs, ISpatialLeafEnumerator* pEnum, int context )
+{
+ Vector cornermin, cornermax;
+
+ while( node >= 0 )
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ // Arbitrary split plane here
+ for (int i = 0; i < 3; ++i)
+ {
+ if (pPlane->normal[i] >= 0)
+ {
+ cornermin[i] = mins[i];
+ cornermax[i] = maxs[i];
+ }
+ else
+ {
+ cornermin[i] = maxs[i];
+ cornermax[i] = mins[i];
+ }
+ }
+
+ if ( (DotProduct( pPlane->normal, cornermax ) - pPlane->dist) <= -TEST_EPSILON )
+ {
+ node = pNode->children[1];
+ }
+ else if ( (DotProduct( pPlane->normal, cornermin ) - pPlane->dist) >= TEST_EPSILON )
+ {
+ node = pNode->children[0];
+ }
+ else
+ {
+ if (!EnumerateLeavesInBox_R( pNode->children[0], mins, maxs, pEnum, context ))
+ {
+ return false;
+ }
+
+ return EnumerateLeavesInBox_R( pNode->children[1], mins, maxs, pEnum, context );
+ }
+ }
+
+ return pEnum->EnumerateLeaf( - node - 1, context );
+}
+
+bool CToolBSPTree::EnumerateLeavesInBox( Vector const& mins, Vector const& maxs,
+ ISpatialLeafEnumerator* pEnum, int context )
+{
+ return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context );
+}
+
+//-----------------------------------------------------------------------------
+// Enumerate leaves within a sphere
+//-----------------------------------------------------------------------------
+
+static bool EnumerateLeavesInSphere_R( int node, Vector const& origin,
+ float radius, ISpatialLeafEnumerator* pEnum, int context )
+{
+ while( node >= 0 )
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ if (DotProduct( pPlane->normal, origin ) + radius - pPlane->dist <= -TEST_EPSILON )
+ {
+ node = pNode->children[1];
+ }
+ else if (DotProduct( pPlane->normal, origin ) - radius - pPlane->dist >= TEST_EPSILON )
+ {
+ node = pNode->children[0];
+ }
+ else
+ {
+ if (!EnumerateLeavesInSphere_R( pNode->children[0],
+ origin, radius, pEnum, context ))
+ {
+ return false;
+ }
+
+ return EnumerateLeavesInSphere_R( pNode->children[1],
+ origin, radius, pEnum, context );
+ }
+ }
+
+ return pEnum->EnumerateLeaf( - node - 1, context );
+}
+
+bool CToolBSPTree::EnumerateLeavesInSphere( Vector const& center, float radius, ISpatialLeafEnumerator* pEnum, int context )
+{
+ return EnumerateLeavesInSphere_R( 0, center, radius, pEnum, context );
+}
+
+
+//-----------------------------------------------------------------------------
+// Enumerate leaves along a ray
+//-----------------------------------------------------------------------------
+
+static bool EnumerateLeavesAlongRay_R( int node, Ray_t const& ray,
+ Vector const& start, Vector const& end, ISpatialLeafEnumerator* pEnum, int context )
+{
+ float front,back;
+
+ while (node >= 0)
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ if ( pPlane->type <= PLANE_Z )
+ {
+ front = start[pPlane->type] - pPlane->dist;
+ back = end[pPlane->type] - pPlane->dist;
+ }
+ else
+ {
+ front = DotProduct(start, pPlane->normal) - pPlane->dist;
+ back = DotProduct(end, pPlane->normal) - pPlane->dist;
+ }
+
+ if (front <= -TEST_EPSILON && back <= -TEST_EPSILON)
+ {
+ node = pNode->children[1];
+ }
+ else if (front >= TEST_EPSILON && back >= TEST_EPSILON)
+ {
+ node = pNode->children[0];
+ }
+ else
+ {
+ // test the front side first
+ bool side = front < 0;
+
+ // Compute intersection point based on the original ray
+ float splitfrac;
+ float denom = DotProduct( ray.m_Delta, pPlane->normal );
+ if ( denom == 0.0f )
+ {
+ splitfrac = 1.0f;
+ }
+ else
+ {
+ splitfrac = ( pPlane->dist - DotProduct( ray.m_Start, pPlane->normal ) ) / denom;
+ if (splitfrac < 0)
+ splitfrac = 0;
+ else if (splitfrac > 1)
+ splitfrac = 1;
+ }
+
+ // Compute the split point
+ Vector split;
+ VectorMA( ray.m_Start, splitfrac, ray.m_Delta, split );
+
+ bool r = EnumerateLeavesAlongRay_R (pNode->children[side], ray, start, split, pEnum, context );
+ if (!r)
+ return r;
+ return EnumerateLeavesAlongRay_R (pNode->children[!side], ray, split, end, pEnum, context);
+ }
+ }
+
+ return pEnum->EnumerateLeaf( - node - 1, context );
+}
+
+bool CToolBSPTree::EnumerateLeavesAlongRay( Ray_t const& ray, ISpatialLeafEnumerator* pEnum, int context )
+{
+ if (!ray.m_IsSwept)
+ {
+ Vector mins, maxs;
+ VectorAdd( ray.m_Start, ray.m_Extents, maxs );
+ VectorSubtract( ray.m_Start, ray.m_Extents, mins );
+
+ return EnumerateLeavesInBox_R( 0, mins, maxs, pEnum, context );
+ }
+
+ // FIXME: Extruded ray not implemented yet
+ Assert( ray.m_IsRay );
+
+ Vector end;
+ VectorAdd( ray.m_Start, ray.m_Delta, end );
+ return EnumerateLeavesAlongRay_R( 0, ray, ray.m_Start, end, pEnum, context );
+}
+
+
+//-----------------------------------------------------------------------------
+// Singleton accessor
+//-----------------------------------------------------------------------------
+
+ISpatialQuery* ToolBSPTree()
+{
+ static CToolBSPTree s_ToolBSPTree;
+ return &s_ToolBSPTree;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Enumerates nodes in front to back order...
+//-----------------------------------------------------------------------------
+
+// FIXME: Do we want this in the IBSPTree interface?
+
+static bool EnumerateNodesAlongRay_R( int node, Ray_t const& ray, float start, float end,
+ IBSPNodeEnumerator* pEnum, int context )
+{
+ float front, back;
+ float startDotN, deltaDotN;
+
+ while (node >= 0)
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ if ( pPlane->type <= PLANE_Z )
+ {
+ startDotN = ray.m_Start[pPlane->type];
+ deltaDotN = ray.m_Delta[pPlane->type];
+ }
+ else
+ {
+ startDotN = DotProduct( ray.m_Start, pPlane->normal );
+ deltaDotN = DotProduct( ray.m_Delta, pPlane->normal );
+ }
+
+ front = startDotN + start * deltaDotN - pPlane->dist;
+ back = startDotN + end * deltaDotN - pPlane->dist;
+
+ if (front <= -TEST_EPSILON && back <= -TEST_EPSILON)
+ {
+ node = pNode->children[1];
+ }
+ else if (front >= TEST_EPSILON && back >= TEST_EPSILON)
+ {
+ node = pNode->children[0];
+ }
+ else
+ {
+ // test the front side first
+ bool side = front < 0;
+
+ // Compute intersection point based on the original ray
+ float splitfrac;
+ if ( deltaDotN == 0.0f )
+ {
+ splitfrac = 1.0f;
+ }
+ else
+ {
+ splitfrac = ( pPlane->dist - startDotN ) / deltaDotN;
+ if (splitfrac < 0.0f)
+ splitfrac = 0.0f;
+ else if (splitfrac > 1.0f)
+ splitfrac = 1.0f;
+ }
+
+ bool r = EnumerateNodesAlongRay_R (pNode->children[side], ray, start, splitfrac, pEnum, context );
+ if (!r)
+ return r;
+
+ // Visit the node...
+ if (!pEnum->EnumerateNode( node, ray, splitfrac, context ))
+ return false;
+
+ return EnumerateNodesAlongRay_R (pNode->children[!side], ray, splitfrac, end, pEnum, context);
+ }
+ }
+
+ // Visit the leaf...
+ return pEnum->EnumerateLeaf( - node - 1, ray, start, end, context );
+}
+
+
+bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context )
+{
+ Vector end;
+ VectorAdd( ray.m_Start, ray.m_Delta, end );
+ return EnumerateNodesAlongRay_R( 0, ray, 0.0f, 1.0f, pEnum, context );
+}
+
+
+//-----------------------------------------------------------------------------
+// Helps us find all leaves associated with a particular cluster
+//-----------------------------------------------------------------------------
+CUtlVector<clusterlist_t> g_ClusterLeaves;
+
+void BuildClusterTable( void )
+{
+ int i, j;
+ int leafCount;
+ int leafList[MAX_MAP_LEAFS];
+
+ g_ClusterLeaves.SetCount( dvis->numclusters );
+ for ( i = 0; i < dvis->numclusters; i++ )
+ {
+ leafCount = 0;
+ for ( j = 0; j < numleafs; j++ )
+ {
+ if ( dleafs[j].cluster == i )
+ {
+ leafList[ leafCount ] = j;
+ leafCount++;
+ }
+ }
+
+ g_ClusterLeaves[i].leafCount = leafCount;
+ if ( leafCount )
+ {
+ g_ClusterLeaves[i].leafs.SetCount( leafCount );
+ memcpy( g_ClusterLeaves[i].leafs.Base(), leafList, sizeof(int) * leafCount );
+ }
+ }
+}
+
+// There's a version of this in host.cpp!!! Make sure that they match.
+void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength )
+{
+ Q_StripExtension( pMapPath, pPlatformMapPath, maxLength );
+
+// if( dxlevel <= 60 )
+// {
+// Q_strncat( pPlatformMapPath, "_dx60", maxLength, COPY_ALL_CHARACTERS );
+// }
+
+ Q_strncat( pPlatformMapPath, ".bsp", maxLength, COPY_ALL_CHARACTERS );
+}
+
+// There's a version of this in checksum_engine.cpp!!! Make sure that they match.
+static bool CRC_MapFile(CRC32_t *crcvalue, const char *pszFileName)
+{
+ byte chunk[1024];
+ lump_t *curLump;
+
+ FileHandle_t fp = g_pFileSystem->Open( pszFileName, "rb" );
+ if ( !fp )
+ return false;
+
+ // CRC across all lumps except for the Entities lump
+ for ( int l = 0; l < HEADER_LUMPS; ++l )
+ {
+ if (l == LUMP_ENTITIES)
+ continue;
+
+ curLump = &g_pBSPHeader->lumps[l];
+ unsigned int nSize = curLump->filelen;
+
+ g_pFileSystem->Seek( fp, curLump->fileofs, FILESYSTEM_SEEK_HEAD );
+
+ // Now read in 1K chunks
+ while ( nSize > 0 )
+ {
+ int nBytesRead = 0;
+
+ if ( nSize > 1024 )
+ nBytesRead = g_pFileSystem->Read( chunk, 1024, fp );
+ else
+ nBytesRead = g_pFileSystem->Read( chunk, nSize, fp );
+
+ // If any data was received, CRC it.
+ if ( nBytesRead > 0 )
+ {
+ nSize -= nBytesRead;
+ CRC32_ProcessBuffer( crcvalue, chunk, nBytesRead );
+ }
+ else
+ {
+ g_pFileSystem->Close( fp );
+ return false;
+ }
+ }
+ }
+
+ g_pFileSystem->Close( fp );
+ return true;
+}
+
+
+void SetHDRMode( bool bHDR )
+{
+ g_bHDR = bHDR;
+ if ( bHDR )
+ {
+ pdlightdata = &dlightdataHDR;
+ g_pLeafAmbientLighting = &g_LeafAmbientLightingHDR;
+ g_pLeafAmbientIndex = &g_LeafAmbientIndexHDR;
+ pNumworldlights = &numworldlightsHDR;
+ dworldlights = dworldlightsHDR;
+#ifdef VRAD
+ extern void VRadDetailProps_SetHDRMode( bool bHDR );
+ VRadDetailProps_SetHDRMode( bHDR );
+#endif
+ }
+ else
+ {
+ pdlightdata = &dlightdataLDR;
+ g_pLeafAmbientLighting = &g_LeafAmbientLightingLDR;
+ g_pLeafAmbientIndex = &g_LeafAmbientIndexLDR;
+ pNumworldlights = &numworldlightsLDR;
+ dworldlights = dworldlightsLDR;
+#ifdef VRAD
+ extern void VRadDetailProps_SetHDRMode( bool bHDR );
+ VRadDetailProps_SetHDRMode( bHDR );
+#endif
+ }
+}
+
+bool SwapVHV( void *pDestBase, void *pSrcBase )
+{
+ byte *pDest = (byte*)pDestBase;
+ byte *pSrc = (byte*)pSrcBase;
+
+ HardwareVerts::FileHeader_t *pHdr = (HardwareVerts::FileHeader_t*)( g_bSwapOnLoad ? pDest : pSrc );
+ g_Swap.SwapFieldsToTargetEndian<HardwareVerts::FileHeader_t>( (HardwareVerts::FileHeader_t*)pDest, (HardwareVerts::FileHeader_t*)pSrc );
+ pSrc += sizeof(HardwareVerts::FileHeader_t);
+ pDest += sizeof(HardwareVerts::FileHeader_t);
+
+ // This swap is pretty format specific
+ Assert( pHdr->m_nVersion == VHV_VERSION );
+ if ( pHdr->m_nVersion != VHV_VERSION )
+ return false;
+
+ HardwareVerts::MeshHeader_t *pSrcMesh = (HardwareVerts::MeshHeader_t*)pSrc;
+ HardwareVerts::MeshHeader_t *pDestMesh = (HardwareVerts::MeshHeader_t*)pDest;
+ HardwareVerts::MeshHeader_t *pMesh = (HardwareVerts::MeshHeader_t*)( g_bSwapOnLoad ? pDest : pSrc );
+ for ( int i = 0; i < pHdr->m_nMeshes; ++i, ++pMesh, ++pSrcMesh, ++pDestMesh )
+ {
+ g_Swap.SwapFieldsToTargetEndian( pDestMesh, pSrcMesh );
+
+ pSrc = (byte*)pSrcBase + pMesh->m_nOffset;
+ pDest = (byte*)pDestBase + pMesh->m_nOffset;
+
+ // Swap as a buffer of integers
+ // (source is bgra for an Intel swap to argb. PowerPC won't swap, so we need argb source.
+ g_Swap.SwapBufferToTargetEndian<int>( (int*)pDest, (int*)pSrc, pMesh->m_nVertexes );
+ }
+ return true;
+}
+
+const char *ResolveStaticPropToModel( const char *pPropName )
+{
+ // resolve back to static prop
+ int iProp = -1;
+
+ // filename should be sp_???.vhv or sp_hdr_???.vhv
+ if ( V_strnicmp( pPropName, "sp_", 3 ) )
+ {
+ return NULL;
+ }
+ const char *pPropNumber = V_strrchr( pPropName, '_' );
+ if ( pPropNumber )
+ {
+ sscanf( pPropNumber+1, "%d.vhv", &iProp );
+ }
+ else
+ {
+ return NULL;
+ }
+
+ // look up the prop to get to the actual model
+ if ( iProp < 0 || iProp >= g_StaticPropInstances.Count() )
+ {
+ // prop out of range
+ return NULL;
+ }
+ int iModel = g_StaticPropInstances[iProp];
+ if ( iModel < 0 || iModel >= g_StaticPropNames.Count() )
+ {
+ // model out of range
+ return NULL;
+ }
+
+ return g_StaticPropNames[iModel].String();
+}
+
+//-----------------------------------------------------------------------------
+// Iterate files in pak file, distribute to converters
+// pak file will be ready for serialization upon completion
+//-----------------------------------------------------------------------------
+void ConvertPakFileContents( const char *pInFilename )
+{
+ IZip *newPakFile = IZip::CreateZip( NULL );
+
+ CUtlBuffer sourceBuf;
+ CUtlBuffer targetBuf;
+ bool bConverted;
+ CUtlVector< CUtlString > hdrFiles;
+
+ int id = -1;
+ int fileSize;
+ while ( 1 )
+ {
+ char relativeName[MAX_PATH];
+ id = GetNextFilename( GetPakFile(), id, relativeName, sizeof( relativeName ), fileSize );
+ if ( id == -1)
+ break;
+
+ bConverted = false;
+ sourceBuf.Purge();
+ targetBuf.Purge();
+
+ const char* pExtension = V_GetFileExtension( relativeName );
+ const char* pExt = 0;
+
+ bool bOK = ReadFileFromPak( GetPakFile(), relativeName, false, sourceBuf );
+ if ( !bOK )
+ {
+ Warning( "Failed to load '%s' from lump pak for conversion or copy in '%s'.\n", relativeName, pInFilename );
+ continue;
+ }
+
+ if ( pExtension && !V_stricmp( pExtension, "vtf" ) )
+ {
+ bOK = g_pVTFConvertFunc( relativeName, sourceBuf, targetBuf, g_pCompressFunc );
+ if ( !bOK )
+ {
+ Warning( "Failed to convert '%s' in '%s'.\n", relativeName, pInFilename );
+ continue;
+ }
+
+ bConverted = true;
+ pExt = ".vtf";
+ }
+ else if ( pExtension && !V_stricmp( pExtension, "vhv" ) )
+ {
+ CUtlBuffer tempBuffer;
+ if ( g_pVHVFixupFunc )
+ {
+ // caller supplied a fixup
+ const char *pModelName = ResolveStaticPropToModel( relativeName );
+ if ( !pModelName )
+ {
+ Warning( "Static Prop '%s' failed to resolve actual model in '%s'.\n", relativeName, pInFilename );
+ continue;
+ }
+
+ // output temp buffer may shrink, must use TellPut() to determine size
+ bOK = g_pVHVFixupFunc( relativeName, pModelName, sourceBuf, tempBuffer );
+ if ( !bOK )
+ {
+ Warning( "Failed to convert '%s' in '%s'.\n", relativeName, pInFilename );
+ continue;
+ }
+ }
+ else
+ {
+ // use the source buffer as-is
+ tempBuffer.EnsureCapacity( sourceBuf.TellMaxPut() );
+ tempBuffer.Put( sourceBuf.Base(), sourceBuf.TellMaxPut() );
+ }
+
+ // swap the VHV
+ targetBuf.EnsureCapacity( tempBuffer.TellPut() );
+ bOK = SwapVHV( targetBuf.Base(), tempBuffer.Base() );
+ if ( !bOK )
+ {
+ Warning( "Failed to swap '%s' in '%s'.\n", relativeName, pInFilename );
+ continue;
+ }
+ targetBuf.SeekPut( CUtlBuffer::SEEK_HEAD, tempBuffer.TellPut() );
+
+ if ( g_pCompressFunc )
+ {
+ CUtlBuffer compressedBuffer;
+ targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, sizeof( HardwareVerts::FileHeader_t ) );
+ bool bCompressed = g_pCompressFunc( targetBuf, compressedBuffer );
+ if ( bCompressed )
+ {
+ // copy all the header data off
+ CUtlBuffer headerBuffer;
+ headerBuffer.EnsureCapacity( sizeof( HardwareVerts::FileHeader_t ) );
+ headerBuffer.Put( targetBuf.Base(), sizeof( HardwareVerts::FileHeader_t ) );
+
+ // reform the target with the header and then the compressed data
+ targetBuf.Clear();
+ targetBuf.Put( headerBuffer.Base(), sizeof( HardwareVerts::FileHeader_t ) );
+ targetBuf.Put( compressedBuffer.Base(), compressedBuffer.TellPut() );
+ }
+
+ targetBuf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ }
+
+ bConverted = true;
+ pExt = ".vhv";
+ }
+
+ if ( !bConverted )
+ {
+ // straight copy
+ AddBufferToPak( newPakFile, relativeName, sourceBuf.Base(), sourceBuf.TellMaxPut(), false );
+ }
+ else
+ {
+ // converted filename
+ V_StripExtension( relativeName, relativeName, sizeof( relativeName ) );
+ V_strcat( relativeName, ".360", sizeof( relativeName ) );
+ V_strcat( relativeName, pExt, sizeof( relativeName ) );
+ AddBufferToPak( newPakFile, relativeName, targetBuf.Base(), targetBuf.TellMaxPut(), false );
+ }
+
+ if ( V_stristr( relativeName, ".hdr" ) || V_stristr( relativeName, "_hdr" ) )
+ {
+ hdrFiles.AddToTail( relativeName );
+ }
+
+ DevMsg( "Created '%s' in lump pak in '%s'.\n", relativeName, pInFilename );
+ }
+
+ // strip ldr version of hdr files
+ for ( int i=0; i<hdrFiles.Count(); i++ )
+ {
+ char ldrFileName[MAX_PATH];
+
+ strcpy( ldrFileName, hdrFiles[i].String() );
+
+ char *pHDRExtension = V_stristr( ldrFileName, ".hdr" );
+ if ( !pHDRExtension )
+ {
+ pHDRExtension = V_stristr( ldrFileName, "_hdr" );
+ }
+
+ if ( pHDRExtension )
+ {
+ // strip .hdr or _hdr to get ldr filename
+ memcpy( pHDRExtension, pHDRExtension+4, strlen( pHDRExtension+4 )+1 );
+
+ DevMsg( "Stripping LDR: %s\n", ldrFileName );
+ newPakFile->RemoveFileFromZip( ldrFileName );
+ }
+ }
+
+ // discard old pak in favor of new pak
+ IZip::ReleaseZip( s_pakFile );
+ s_pakFile = newPakFile;
+}
+
+void SetAlignedLumpPosition( int lumpnum, int alignment = LUMP_ALIGNMENT )
+{
+ g_pBSPHeader->lumps[lumpnum].fileofs = AlignFilePosition( g_hBSPFile, alignment );
+}
+
+template< class T >
+int SwapLumpToDisk( int fieldType, int lumpnum )
+{
+ if ( g_pBSPHeader->lumps[lumpnum].filelen == 0 )
+ return 0;
+
+ DevMsg( "Swapping %s\n", GetLumpName( lumpnum ) );
+
+ // lump swap may expand, allocate enough expansion room
+ void *pBuffer = malloc( 2*g_pBSPHeader->lumps[lumpnum].filelen );
+
+ // CopyLumpInternal will handle the swap on load case
+ unsigned int fieldSize = ( fieldType == FIELD_VECTOR ) ? sizeof(Vector) : sizeof(T);
+ unsigned int count = CopyLumpInternal<T>( fieldType, lumpnum, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].version );
+ g_pBSPHeader->lumps[lumpnum].filelen = count * fieldSize;
+
+ if ( g_bSwapOnWrite )
+ {
+ // Swap the lump in place before writing
+ switch( lumpnum )
+ {
+ case LUMP_VISIBILITY:
+ SwapVisibilityLump( (byte*)pBuffer, (byte*)pBuffer, count );
+ break;
+
+ case LUMP_PHYSCOLLIDE:
+ // SwapPhyscollideLump may change size
+ SwapPhyscollideLump( (byte*)pBuffer, (byte*)pBuffer, count );
+ g_pBSPHeader->lumps[lumpnum].filelen = count;
+ break;
+
+ case LUMP_PHYSDISP:
+ SwapPhysdispLump( (byte*)pBuffer, (byte*)pBuffer, count );
+ break;
+
+ default:
+ g_Swap.SwapBufferToTargetEndian( (T*)pBuffer, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].filelen / sizeof(T) );
+ break;
+ }
+ }
+
+ SetAlignedLumpPosition( lumpnum );
+ SafeWrite( g_hBSPFile, pBuffer, g_pBSPHeader->lumps[lumpnum].filelen );
+
+ free( pBuffer );
+
+ return g_pBSPHeader->lumps[lumpnum].filelen;
+}
+
+template< class T >
+int SwapLumpToDisk( int lumpnum )
+{
+ if ( g_pBSPHeader->lumps[lumpnum].filelen == 0 || g_Lumps.bLumpParsed[lumpnum] )
+ return 0;
+
+ DevMsg( "Swapping %s\n", GetLumpName( lumpnum ) );
+
+ // lump swap may expand, allocate enough room
+ void *pBuffer = malloc( 2*g_pBSPHeader->lumps[lumpnum].filelen );
+
+ // CopyLumpInternal will handle the swap on load case
+ int count = CopyLumpInternal<T>( lumpnum, (T*)pBuffer, g_pBSPHeader->lumps[lumpnum].version );
+ g_pBSPHeader->lumps[lumpnum].filelen = count * sizeof(T);
+
+ if ( g_bSwapOnWrite )
+ {
+ // Swap the lump in place before writing
+ g_Swap.SwapFieldsToTargetEndian( (T*)pBuffer, (T*)pBuffer, count );
+ }
+
+ SetAlignedLumpPosition( lumpnum );
+ SafeWrite( g_hBSPFile, pBuffer, g_pBSPHeader->lumps[lumpnum].filelen );
+ free( pBuffer );
+
+ return g_pBSPHeader->lumps[lumpnum].filelen;
+}
+
+void SwapLeafAmbientLightingLumpToDisk()
+{
+ if ( HasLump( LUMP_LEAF_AMBIENT_INDEX ) || HasLump( LUMP_LEAF_AMBIENT_INDEX_HDR ) )
+ {
+ // current version, swap in place
+ if ( HasLump( LUMP_LEAF_AMBIENT_INDEX_HDR ) )
+ {
+ // write HDR
+ SwapLumpToDisk< dleafambientlighting_t >( LUMP_LEAF_AMBIENT_LIGHTING_HDR );
+ SwapLumpToDisk< dleafambientindex_t >( LUMP_LEAF_AMBIENT_INDEX_HDR );
+
+ // cull LDR
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = 0;
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = 0;
+ }
+ else
+ {
+ // no HDR, keep LDR version
+ SwapLumpToDisk< dleafambientlighting_t >( LUMP_LEAF_AMBIENT_LIGHTING );
+ SwapLumpToDisk< dleafambientindex_t >( LUMP_LEAF_AMBIENT_INDEX );
+ }
+ }
+ else
+ {
+ // older ambient lighting version (before index)
+ // load older ambient lighting into memory and build ambient/index
+ // an older leaf version would have already built the new LDR leaf ambient/index
+ int numLeafs = g_pBSPHeader->lumps[LUMP_LEAFS].filelen / sizeof( dleaf_t );
+ LoadLeafAmbientLighting( numLeafs );
+
+ if ( HasLump( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) )
+ {
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_LIGHTING_HDR ) );
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_INDEX_HDR ) );
+
+ // write HDR
+ if ( g_bSwapOnWrite )
+ {
+ g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientLightingHDR.Base(), g_LeafAmbientLightingHDR.Count() );
+ g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientIndexHDR.Base(), g_LeafAmbientIndexHDR.Count() );
+ }
+
+ SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_LIGHTING_HDR );
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].version = LUMP_LEAF_AMBIENT_LIGHTING_VERSION;
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].filelen = g_LeafAmbientLightingHDR.Count() * sizeof( dleafambientlighting_t );
+ SafeWrite( g_hBSPFile, g_LeafAmbientLightingHDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING_HDR].filelen );
+
+ SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_INDEX_HDR );
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX_HDR].filelen = g_LeafAmbientIndexHDR.Count() * sizeof( dleafambientindex_t );
+ SafeWrite( g_hBSPFile, g_LeafAmbientIndexHDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX_HDR].filelen );
+
+ // mark as processed
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING_HDR] = true;
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX_HDR] = true;
+
+ // cull LDR
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = 0;
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = 0;
+ }
+ else
+ {
+ // no HDR, keep LDR version
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_LIGHTING ) );
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAF_AMBIENT_INDEX ) );
+
+ if ( g_bSwapOnWrite )
+ {
+ g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientLightingLDR.Base(), g_LeafAmbientLightingLDR.Count() );
+ g_Swap.SwapFieldsToTargetEndian( g_LeafAmbientIndexLDR.Base(), g_LeafAmbientIndexLDR.Count() );
+ }
+
+ SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_LIGHTING );
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].version = LUMP_LEAF_AMBIENT_LIGHTING_VERSION;
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen = g_LeafAmbientLightingLDR.Count() * sizeof( dleafambientlighting_t );
+ SafeWrite( g_hBSPFile, g_LeafAmbientLightingLDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_LIGHTING].filelen );
+
+ SetAlignedLumpPosition( LUMP_LEAF_AMBIENT_INDEX );
+ g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen = g_LeafAmbientIndexLDR.Count() * sizeof( dleafambientindex_t );
+ SafeWrite( g_hBSPFile, g_LeafAmbientIndexLDR.Base(), g_pBSPHeader->lumps[LUMP_LEAF_AMBIENT_INDEX].filelen );
+
+ // mark as processed
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_LIGHTING] = true;
+ g_Lumps.bLumpParsed[LUMP_LEAF_AMBIENT_INDEX] = true;
+ }
+
+ g_LeafAmbientLightingLDR.Purge();
+ g_LeafAmbientIndexLDR.Purge();
+ g_LeafAmbientLightingHDR.Purge();
+ g_LeafAmbientIndexHDR.Purge();
+ }
+}
+
+void SwapLeafLumpToDisk( void )
+{
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_LEAFS ) );
+
+ // load the leafs
+ int count = LoadLeafs();
+ if ( g_bSwapOnWrite )
+ {
+ g_Swap.SwapFieldsToTargetEndian( dleafs, count );
+ }
+
+ bool bOldLeafVersion = ( LumpVersion( LUMP_LEAFS ) == 0 );
+ if ( bOldLeafVersion )
+ {
+ // version has been converted in the load process
+ // not updating the version ye, SwapLeafAmbientLightingLumpToDisk() can detect
+ g_pBSPHeader->lumps[LUMP_LEAFS].filelen = count * sizeof( dleaf_t );
+ }
+
+ SetAlignedLumpPosition( LUMP_LEAFS );
+ SafeWrite( g_hBSPFile, dleafs, g_pBSPHeader->lumps[LUMP_LEAFS].filelen );
+
+ SwapLeafAmbientLightingLumpToDisk();
+
+ if ( bOldLeafVersion )
+ {
+ // version has been converted in the load process
+ // can now safely change
+ g_pBSPHeader->lumps[LUMP_LEAFS].version = 1;
+ }
+
+#if defined( BSP_USE_LESS_MEMORY )
+ if ( dleafs )
+ {
+ free( dleafs );
+ dleafs = NULL;
+ }
+#endif
+}
+
+void SwapOcclusionLumpToDisk( void )
+{
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_OCCLUSION ) );
+
+ LoadOcclusionLump();
+ SetAlignedLumpPosition( LUMP_OCCLUSION );
+ AddOcclusionLump();
+}
+
+void SwapPakfileLumpToDisk( const char *pInFilename )
+{
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_PAKFILE ) );
+
+ byte *pakbuffer = NULL;
+ int paksize = CopyVariableLump<byte>( FIELD_CHARACTER, LUMP_PAKFILE, ( void ** )&pakbuffer );
+ if ( paksize > 0 )
+ {
+ GetPakFile()->ActivateByteSwapping( IsX360() );
+ GetPakFile()->ParseFromBuffer( pakbuffer, paksize );
+
+ ConvertPakFileContents( pInFilename );
+ }
+ free( pakbuffer );
+
+ SetAlignedLumpPosition( LUMP_PAKFILE, XBOX_DVD_SECTORSIZE );
+ WritePakFileLump();
+
+ ReleasePakFileLumps();
+}
+
+void SwapGameLumpsToDisk( void )
+{
+ DevMsg( "Swapping %s\n", GetLumpName( LUMP_GAME_LUMP ) );
+
+ g_GameLumps.ParseGameLump( g_pBSPHeader );
+ SetAlignedLumpPosition( LUMP_GAME_LUMP );
+ AddGameLumps();
+}
+
+//-----------------------------------------------------------------------------
+// Generate a table of all static props, used for resolving static prop lighting
+// files back to their actual mdl.
+//-----------------------------------------------------------------------------
+void BuildStaticPropNameTable()
+{
+ g_StaticPropNames.Purge();
+ g_StaticPropInstances.Purge();
+
+ g_GameLumps.ParseGameLump( g_pBSPHeader );
+
+ GameLumpHandle_t hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
+ if ( hGameLump != g_GameLumps.InvalidGameLump() )
+ {
+ int nVersion = g_GameLumps.GetGameLumpVersion( hGameLump );
+ if ( nVersion < 4 )
+ {
+ // old unsupported version
+ return;
+ }
+
+ if ( nVersion != 4 && nVersion != 5 && nVersion != 6 )
+ {
+ Error( "Unknown Static Prop Lump version %d!\n", nVersion );
+ }
+
+ byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump );
+ if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) )
+ {
+ // get the model dictionary
+ int count = ((int *)pGameLumpData)[0];
+ pGameLumpData += sizeof( int );
+ StaticPropDictLump_t *pStaticPropDictLump = (StaticPropDictLump_t *)pGameLumpData;
+ for ( int i = 0; i < count; i++ )
+ {
+ g_StaticPropNames.AddToTail( pStaticPropDictLump[i].m_Name );
+ }
+ pGameLumpData += count * sizeof( StaticPropDictLump_t );
+
+ // skip the leaf list
+ count = ((int *)pGameLumpData)[0];
+ pGameLumpData += sizeof( int );
+ pGameLumpData += count * sizeof( StaticPropLeafLump_t );
+
+ // get the instances
+ count = ((int *)pGameLumpData)[0];
+ pGameLumpData += sizeof( int );
+ for ( int i = 0; i < count; i++ )
+ {
+ int propType;
+ if ( nVersion == 4 )
+ {
+ propType = ((StaticPropLumpV4_t *)pGameLumpData)->m_PropType;
+ pGameLumpData += sizeof( StaticPropLumpV4_t );
+ }
+ else if ( nVersion == 5 )
+ {
+ propType = ((StaticPropLumpV5_t *)pGameLumpData)->m_PropType;
+ pGameLumpData += sizeof( StaticPropLumpV5_t );
+ }
+ else
+ {
+ propType = ((StaticPropLump_t *)pGameLumpData)->m_PropType;
+ pGameLumpData += sizeof( StaticPropLump_t );
+ }
+ g_StaticPropInstances.AddToTail( propType );
+ }
+ }
+ }
+
+ g_GameLumps.DestroyAllGameLumps();
+}
+
+int AlignBuffer( CUtlBuffer &buffer, int alignment )
+{
+ unsigned int newPosition = AlignValue( buffer.TellPut(), alignment );
+ int padLength = newPosition - buffer.TellPut();
+ for ( int i = 0; i<padLength; i++ )
+ {
+ buffer.PutChar( '\0' );
+ }
+ return buffer.TellPut();
+}
+
+struct SortedLump_t
+{
+ int lumpNum;
+ lump_t *pLump;
+};
+
+int SortLumpsByOffset( const SortedLump_t *pSortedLumpA, const SortedLump_t *pSortedLumpB )
+{
+ int fileOffsetA = pSortedLumpA->pLump->fileofs;
+ int fileOffsetB = pSortedLumpB->pLump->fileofs;
+
+ int fileSizeA = pSortedLumpA->pLump->filelen;
+ int fileSizeB = pSortedLumpB->pLump->filelen;
+
+ // invalid or empty lumps get sorted together
+ if ( !fileSizeA )
+ {
+ fileOffsetA = 0;
+ }
+ if ( !fileSizeB )
+ {
+ fileOffsetB = 0;
+ }
+
+ // compare by offset, want ascending
+ if ( fileOffsetA < fileOffsetB )
+ {
+ return -1;
+ }
+ else if ( fileOffsetA > fileOffsetB )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+bool CompressGameLump( dheader_t *pInBSPHeader, dheader_t *pOutBSPHeader, CUtlBuffer &outputBuffer, CompressFunc_t pCompressFunc )
+{
+ CByteswap byteSwap;
+
+ dgamelumpheader_t* pInGameLumpHeader = (dgamelumpheader_t*)(((byte *)pInBSPHeader) + pInBSPHeader->lumps[LUMP_GAME_LUMP].fileofs);
+ dgamelump_t* pInGameLump = (dgamelump_t*)(pInGameLumpHeader + 1);
+
+ byteSwap.ActivateByteSwapping( true );
+ byteSwap.SwapFieldsToTargetEndian( pInGameLumpHeader );
+ byteSwap.SwapFieldsToTargetEndian( pInGameLump, pInGameLumpHeader->lumpCount );
+
+ unsigned int newOffset = outputBuffer.TellPut();
+ outputBuffer.Put( pInGameLumpHeader, sizeof( dgamelumpheader_t ) );
+ outputBuffer.Put( pInGameLump, pInGameLumpHeader->lumpCount * sizeof( dgamelump_t ) );
+
+ dgamelumpheader_t* pOutGameLumpHeader = (dgamelumpheader_t*)((byte *)outputBuffer.Base() + newOffset);
+ dgamelump_t* pOutGameLump = (dgamelump_t*)(pOutGameLumpHeader + 1);
+
+ // add a dummy terminal gamelump
+ // purposely NOT updating the .filelen to reflect the compressed size, but leaving as original size
+ // callers use the next entry offset to determine compressed size
+ pOutGameLumpHeader->lumpCount++;
+ dgamelump_t dummyLump = { 0 };
+ outputBuffer.Put( &dummyLump, sizeof( dgamelump_t ) );
+
+ for ( int i = 0; i < pInGameLumpHeader->lumpCount; i++ )
+ {
+ CUtlBuffer inputBuffer;
+ CUtlBuffer compressedBuffer;
+
+ pOutGameLump[i].fileofs = AlignBuffer( outputBuffer, 4 );
+
+ if ( pInGameLump[i].filelen )
+ {
+ inputBuffer.SetExternalBuffer( ((byte *)pInBSPHeader) + pInGameLump[i].fileofs, pInGameLump[i].filelen, pInGameLump[i].filelen );
+
+ bool bCompressed = pCompressFunc( inputBuffer, compressedBuffer );
+ if ( bCompressed )
+ {
+ pOutGameLump[i].flags |= GAMELUMPFLAG_COMPRESSED;
+
+ outputBuffer.Put( compressedBuffer.Base(), compressedBuffer.TellPut() );
+ compressedBuffer.Purge();
+ }
+ else
+ {
+ // as is
+ outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() );
+ }
+ }
+ }
+
+ // fix the dummy terminal lump
+ int lastLump = pOutGameLumpHeader->lumpCount-1;
+ pOutGameLump[lastLump].fileofs = outputBuffer.TellPut();
+
+ // fix the output for 360, swapping it back
+ byteSwap.SwapFieldsToTargetEndian( pOutGameLump, pOutGameLumpHeader->lumpCount );
+ byteSwap.SwapFieldsToTargetEndian( pOutGameLumpHeader );
+
+ pOutBSPHeader->lumps[LUMP_GAME_LUMP].fileofs = newOffset;
+ pOutBSPHeader->lumps[LUMP_GAME_LUMP].filelen = outputBuffer.TellPut() - newOffset;
+
+ return true;
+}
+
+bool CompressBSP( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer, CompressFunc_t pCompressFunc )
+{
+ CByteswap byteSwap;
+
+ dheader_t *pInBSPHeader = (dheader_t *)inputBuffer.Base();
+ if ( pInBSPHeader->ident != BigLong( IDBSPHEADER ) || !pCompressFunc )
+ {
+ // only compress 360 bsp's
+ return false;
+ }
+
+ // bsp is 360, swap the header back
+ byteSwap.ActivateByteSwapping( true );
+ byteSwap.SwapFieldsToTargetEndian( pInBSPHeader );
+
+ // output will be smaller, use input size as upper bound
+ outputBuffer.EnsureCapacity( inputBuffer.TellMaxPut() );
+ outputBuffer.Put( pInBSPHeader, sizeof( dheader_t ) );
+
+ dheader_t *pOutBSPHeader = (dheader_t *)outputBuffer.Base();
+
+ // must adhere to input lump's offset order and process according to that, NOT lump num
+ // sort by offset order
+ CUtlVector< SortedLump_t > sortedLumps;
+ for ( int i = 0; i < HEADER_LUMPS; i++ )
+ {
+ int iIndex = sortedLumps.AddToTail();
+ sortedLumps[iIndex].lumpNum = i;
+ sortedLumps[iIndex].pLump = &pInBSPHeader->lumps[i];
+ }
+ sortedLumps.Sort( SortLumpsByOffset );
+
+ // iterate in sorted order
+ for ( int i = 0; i < HEADER_LUMPS; ++i )
+ {
+ SortedLump_t *pSortedLump = &sortedLumps[i];
+ int lumpNum = pSortedLump->lumpNum;
+
+ if ( !pSortedLump->pLump->filelen )
+ {
+ // degenerate
+ pOutBSPHeader->lumps[lumpNum].fileofs = 0;
+ }
+ else
+ {
+ int alignment = 4;
+ if ( lumpNum == LUMP_PAKFILE )
+ {
+ alignment = 2048;
+ }
+ unsigned int newOffset = AlignBuffer( outputBuffer, alignment );
+
+ // only set by compressed lumps, hides the uncompressed size
+ *((unsigned int *)pOutBSPHeader->lumps[lumpNum].fourCC) = 0;
+
+ CUtlBuffer inputBuffer;
+ inputBuffer.SetExternalBuffer( ((byte *)pInBSPHeader) + pSortedLump->pLump->fileofs, pSortedLump->pLump->filelen, pSortedLump->pLump->filelen );
+
+ if ( lumpNum == LUMP_GAME_LUMP )
+ {
+ // the game lump has to have each of its components individually compressed
+ CompressGameLump( pInBSPHeader, pOutBSPHeader, outputBuffer, pCompressFunc );
+ }
+ else if ( lumpNum == LUMP_PAKFILE )
+ {
+ // add as is
+ pOutBSPHeader->lumps[lumpNum].fileofs = newOffset;
+ outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() );
+ }
+ else
+ {
+ CUtlBuffer compressedBuffer;
+ bool bCompressed = pCompressFunc( inputBuffer, compressedBuffer );
+ if ( bCompressed )
+ {
+ // placing the uncompressed size in the unused fourCC, will decode at runtime
+ *((unsigned int *)pOutBSPHeader->lumps[lumpNum].fourCC) = BigLong( inputBuffer.TellPut() );
+ pOutBSPHeader->lumps[lumpNum].filelen = compressedBuffer.TellPut();
+ pOutBSPHeader->lumps[lumpNum].fileofs = newOffset;
+ outputBuffer.Put( compressedBuffer.Base(), compressedBuffer.TellPut() );
+ compressedBuffer.Purge();
+ }
+ else
+ {
+ // add as is
+ pOutBSPHeader->lumps[lumpNum].fileofs = newOffset;
+ outputBuffer.Put( inputBuffer.Base(), inputBuffer.TellPut() );
+ }
+ }
+ }
+ }
+
+ // fix the output for 360, swapping it back
+ byteSwap.SetTargetBigEndian( true );
+ byteSwap.SwapFieldsToTargetEndian( pOutBSPHeader );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// For all lumps in a bsp: Loads the lump from file A, swaps it, writes it to file B.
+// This limits the memory used for the swap process which helps the Xbox 360.
+//
+// NOTE: These lumps will be written to the file in exactly the order they appear here,
+// so they can be shifted around if desired for file access optimization.
+//-----------------------------------------------------------------------------
+bool SwapBSPFile( const char *pInFilename, const char *pOutFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc )
+{
+ DevMsg( "Creating %s\n", pOutFilename );
+
+ if ( !g_pFileSystem->FileExists( pInFilename ) )
+ {
+ Warning( "Error! Couldn't open input file %s - BSP swap failed!\n", pInFilename );
+ return false;
+ }
+
+ g_hBSPFile = SafeOpenWrite( pOutFilename );
+ if ( !g_hBSPFile )
+ {
+ Warning( "Error! Couldn't open output file %s - BSP swap failed!\n", pOutFilename );
+ return false;
+ }
+
+ if ( !pVTFConvertFunc )
+ {
+ Warning( "Error! Missing VTF Conversion function\n" );
+ return false;
+ }
+ g_pVTFConvertFunc = pVTFConvertFunc;
+
+ // optional VHV fixup
+ g_pVHVFixupFunc = pVHVFixupFunc;
+
+ // optional compression callback
+ g_pCompressFunc = pCompressFunc;
+
+ // These must be mutually exclusive
+ g_bSwapOnLoad = bSwapOnLoad;
+ g_bSwapOnWrite = !bSwapOnLoad;
+
+ g_Swap.ActivateByteSwapping( true );
+
+ OpenBSPFile( pInFilename );
+
+ // CRC the bsp first
+ CRC32_t mapCRC;
+ CRC32_Init(&mapCRC);
+ if ( !CRC_MapFile( &mapCRC, pInFilename ) )
+ {
+ Warning( "Failed to CRC the bsp\n" );
+ return false;
+ }
+
+ // hold a dictionary of all the static prop names
+ // this is needed to properly convert any VHV files inside the pak lump
+ BuildStaticPropNameTable();
+
+ // Set the output file pointer after the header
+ dheader_t dummyHeader = { 0 };
+ SafeWrite( g_hBSPFile, &dummyHeader, sizeof( dheader_t ) );
+
+ // To allow for alignment fixups, the lumps will be written to the
+ // output file in the order they appear in this function.
+
+ // NOTE: Flags for 360 !!!MUST!!! be first
+ SwapLumpToDisk< dflagslump_t >( LUMP_MAP_FLAGS );
+
+ // complex lump swaps first or for later contingent data
+ SwapLeafLumpToDisk();
+ SwapOcclusionLumpToDisk();
+ SwapGameLumpsToDisk();
+
+ // Strip dead or non relevant lumps
+ g_pBSPHeader->lumps[LUMP_DISP_LIGHTMAP_ALPHAS].filelen = 0;
+ g_pBSPHeader->lumps[LUMP_FACEIDS].filelen = 0;
+
+ // Strip obsolete LDR in favor of HDR
+ if ( SwapLumpToDisk<dface_t>( LUMP_FACES_HDR ) )
+ {
+ g_pBSPHeader->lumps[LUMP_FACES].filelen = 0;
+ }
+ else
+ {
+ // no HDR, keep LDR version
+ SwapLumpToDisk<dface_t>( LUMP_FACES );
+ }
+
+ if ( SwapLumpToDisk<dworldlight_t>( LUMP_WORLDLIGHTS_HDR ) )
+ {
+ g_pBSPHeader->lumps[LUMP_WORLDLIGHTS].filelen = 0;
+ }
+ else
+ {
+ // no HDR, keep LDR version
+ SwapLumpToDisk<dworldlight_t>( LUMP_WORLDLIGHTS );
+ }
+
+ // Simple lump swaps
+ SwapLumpToDisk<byte>( FIELD_CHARACTER, LUMP_PHYSDISP );
+ SwapLumpToDisk<byte>( FIELD_CHARACTER, LUMP_PHYSCOLLIDE );
+ SwapLumpToDisk<byte>( FIELD_CHARACTER, LUMP_VISIBILITY );
+ SwapLumpToDisk<dmodel_t>( LUMP_MODELS );
+ SwapLumpToDisk<dvertex_t>( LUMP_VERTEXES );
+ SwapLumpToDisk<dplane_t>( LUMP_PLANES );
+ SwapLumpToDisk<dnode_t>( LUMP_NODES );
+ SwapLumpToDisk<texinfo_t>( LUMP_TEXINFO );
+ SwapLumpToDisk<dtexdata_t>( LUMP_TEXDATA );
+ SwapLumpToDisk<ddispinfo_t>( LUMP_DISPINFO );
+ SwapLumpToDisk<CDispVert>( LUMP_DISP_VERTS );
+ SwapLumpToDisk<CDispTri>( LUMP_DISP_TRIS );
+ SwapLumpToDisk<char>( FIELD_CHARACTER, LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS );
+ SwapLumpToDisk<CFaceMacroTextureInfo>( LUMP_FACE_MACRO_TEXTURE_INFO );
+ SwapLumpToDisk<dprimitive_t>( LUMP_PRIMITIVES );
+ SwapLumpToDisk<dprimvert_t>( LUMP_PRIMVERTS );
+ SwapLumpToDisk<unsigned short>( FIELD_SHORT, LUMP_PRIMINDICES );
+ SwapLumpToDisk<dface_t>( LUMP_ORIGINALFACES );
+ SwapLumpToDisk<unsigned short>( FIELD_SHORT, LUMP_LEAFFACES );
+ SwapLumpToDisk<unsigned short>( FIELD_SHORT, LUMP_LEAFBRUSHES );
+ SwapLumpToDisk<int>( FIELD_INTEGER, LUMP_SURFEDGES );
+ SwapLumpToDisk<dedge_t>( LUMP_EDGES );
+ SwapLumpToDisk<dbrush_t>( LUMP_BRUSHES );
+ SwapLumpToDisk<dbrushside_t>( LUMP_BRUSHSIDES );
+ SwapLumpToDisk<darea_t>( LUMP_AREAS );
+ SwapLumpToDisk<dareaportal_t>( LUMP_AREAPORTALS );
+ SwapLumpToDisk<char>( FIELD_CHARACTER, LUMP_ENTITIES );
+ SwapLumpToDisk<dleafwaterdata_t>( LUMP_LEAFWATERDATA );
+ SwapLumpToDisk<float>( FIELD_VECTOR, LUMP_VERTNORMALS );
+ SwapLumpToDisk<short>( FIELD_SHORT, LUMP_VERTNORMALINDICES );
+ SwapLumpToDisk<float>( FIELD_VECTOR, LUMP_CLIPPORTALVERTS );
+ SwapLumpToDisk<dcubemapsample_t>( LUMP_CUBEMAPS );
+ SwapLumpToDisk<char>( FIELD_CHARACTER, LUMP_TEXDATA_STRING_DATA );
+ SwapLumpToDisk<int>( FIELD_INTEGER, LUMP_TEXDATA_STRING_TABLE );
+ SwapLumpToDisk<doverlay_t>( LUMP_OVERLAYS );
+ SwapLumpToDisk<dwateroverlay_t>( LUMP_WATEROVERLAYS );
+ SwapLumpToDisk<unsigned short>( FIELD_SHORT, LUMP_LEAFMINDISTTOWATER );
+ SwapLumpToDisk<doverlayfade_t>( LUMP_OVERLAY_FADES );
+
+
+ // NOTE: this data placed at the end for the sake of 360:
+ {
+ // NOTE: lighting must be the penultimate lump
+ // (allows 360 to free this memory part-way through map loading)
+ if ( SwapLumpToDisk<byte>( FIELD_CHARACTER, LUMP_LIGHTING_HDR ) )
+ {
+ g_pBSPHeader->lumps[LUMP_LIGHTING].filelen = 0;
+ }
+ else
+ {
+ // no HDR, keep LDR version
+ SwapLumpToDisk<byte>( FIELD_CHARACTER, LUMP_LIGHTING );
+ }
+ // NOTE: Pakfile for 360 !!!MUST!!! be last
+ SwapPakfileLumpToDisk( pInFilename );
+ }
+
+
+ // Store the crc in the flags lump version field
+ g_pBSPHeader->lumps[LUMP_MAP_FLAGS].version = mapCRC;
+
+ // Pad out the end of the file to a sector boundary for optimal IO
+ AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE );
+
+ // Warn of any lumps that didn't get swapped
+ for ( int i = 0; i < HEADER_LUMPS; ++i )
+ {
+ if ( HasLump( i ) && !g_Lumps.bLumpParsed[i] )
+ {
+ // a new lump got added that needs to have a swap function
+ Warning( "BSP: '%s', %s has no swap or copy function. Discarding!\n", pInFilename, GetLumpName(i) );
+
+ // the data didn't get copied, so don't reference garbage
+ g_pBSPHeader->lumps[i].filelen = 0;
+ }
+ }
+
+ // Write the updated header
+ g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD );
+ WriteData( g_pBSPHeader );
+ g_pFileSystem->Close( g_hBSPFile );
+ g_hBSPFile = 0;
+
+ // Cleanup
+ g_Swap.ActivateByteSwapping( false );
+
+ CloseBSPFile();
+
+ g_StaticPropNames.Purge();
+ g_StaticPropInstances.Purge();
+
+ DevMsg( "Finished BSP Swap\n" );
+
+ // caller provided compress func will further compress compatible lumps
+ if ( pCompressFunc )
+ {
+ CUtlBuffer inputBuffer;
+ if ( !g_pFileSystem->ReadFile( pOutFilename, NULL, inputBuffer ) )
+ {
+ Warning( "Error! Couldn't read file %s - final BSP compression failed!\n", pOutFilename );
+ return false;
+ }
+
+ CUtlBuffer outputBuffer;
+ if ( !CompressBSP( inputBuffer, outputBuffer, pCompressFunc ) )
+ {
+ Warning( "Error! Failed to compress BSP '%s'!\n", pOutFilename );
+ return false;
+ }
+
+ g_hBSPFile = SafeOpenWrite( pOutFilename );
+ if ( !g_hBSPFile )
+ {
+ Warning( "Error! Couldn't open output file %s - BSP swap failed!\n", pOutFilename );
+ return false;
+ }
+ SafeWrite( g_hBSPFile, outputBuffer.Base(), outputBuffer.TellPut() );
+ g_pFileSystem->Close( g_hBSPFile );
+ g_hBSPFile = 0;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Get the pak lump from a BSP
+//-----------------------------------------------------------------------------
+bool GetPakFileLump( const char *pBSPFilename, void **pPakData, int *pPakSize )
+{
+ *pPakData = NULL;
+ *pPakSize = 0;
+
+ if ( !g_pFileSystem->FileExists( pBSPFilename ) )
+ {
+ Warning( "Error! Couldn't open file %s!\n", pBSPFilename );
+ return false;
+ }
+
+ // determine endian nature
+ dheader_t *pHeader;
+ LoadFile( pBSPFilename, (void **)&pHeader );
+ bool bSwap = ( pHeader->ident == BigLong( IDBSPHEADER ) );
+ free( pHeader );
+
+ g_bSwapOnLoad = bSwap;
+ g_bSwapOnWrite = !bSwap;
+
+ OpenBSPFile( pBSPFilename );
+
+ if ( g_pBSPHeader->lumps[LUMP_PAKFILE].filelen )
+ {
+ *pPakSize = CopyVariableLump<byte>( FIELD_CHARACTER, LUMP_PAKFILE, pPakData );
+ }
+
+ CloseBSPFile();
+
+ return true;
+}
+
+// compare function for qsort below
+static int LumpOffsetCompare( const void *pElem1, const void *pElem2 )
+{
+ int lump1 = *(byte *)pElem1;
+ int lump2 = *(byte *)pElem2;
+
+ if ( lump1 != lump2 )
+ {
+ // force LUMP_MAP_FLAGS to be first, always
+ if ( lump1 == LUMP_MAP_FLAGS )
+ {
+ return -1;
+ }
+ else if ( lump2 == LUMP_MAP_FLAGS )
+ {
+ return 1;
+ }
+
+ // force LUMP_PAKFILE to be last, always
+ if ( lump1 == LUMP_PAKFILE )
+ {
+ return 1;
+ }
+ else if ( lump2 == LUMP_PAKFILE )
+ {
+ return -1;
+ }
+ }
+
+ int fileOffset1 = g_pBSPHeader->lumps[lump1].fileofs;
+ int fileOffset2 = g_pBSPHeader->lumps[lump2].fileofs;
+
+ // invalid or empty lumps will get sorted together
+ if ( !g_pBSPHeader->lumps[lump1].filelen )
+ {
+ fileOffset1 = 0;
+ }
+
+ if ( !g_pBSPHeader->lumps[lump2].filelen )
+ {
+ fileOffset2 = 0;
+ }
+
+ // compare by offset
+ if ( fileOffset1 < fileOffset2 )
+ {
+ return -1;
+ }
+ else if ( fileOffset1 > fileOffset2 )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Replace the pak lump in a BSP
+//-----------------------------------------------------------------------------
+bool SetPakFileLump( const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize )
+{
+ if ( !g_pFileSystem->FileExists( pBSPFilename ) )
+ {
+ Warning( "Error! Couldn't open file %s!\n", pBSPFilename );
+ return false;
+ }
+
+ // determine endian nature
+ dheader_t *pHeader;
+ LoadFile( pBSPFilename, (void **)&pHeader );
+ bool bSwap = ( pHeader->ident == BigLong( IDBSPHEADER ) );
+ free( pHeader );
+
+ g_bSwapOnLoad = bSwap;
+ g_bSwapOnWrite = bSwap;
+
+ OpenBSPFile( pBSPFilename );
+
+ // save a copy of the old header
+ // generating a new bsp is a destructive operation
+ dheader_t oldHeader;
+ oldHeader = *g_pBSPHeader;
+
+ g_hBSPFile = SafeOpenWrite( pNewFilename );
+ if ( !g_hBSPFile )
+ {
+ return false;
+ }
+
+ // placeholder only, reset at conclusion
+ WriteData( &oldHeader );
+
+ // lumps must be reserialized in same relative offset order
+ // build sorted order table
+ int readOrder[HEADER_LUMPS];
+ for ( int i=0; i<HEADER_LUMPS; i++ )
+ {
+ readOrder[i] = i;
+ }
+ qsort( readOrder, HEADER_LUMPS, sizeof( int ), LumpOffsetCompare );
+
+ for ( int i = 0; i < HEADER_LUMPS; i++ )
+ {
+ int lump = readOrder[i];
+
+ if ( lump == LUMP_PAKFILE )
+ {
+ // pak lump always written last, with special alignment
+ continue;
+ }
+
+ int length = g_pBSPHeader->lumps[lump].filelen;
+ if ( length )
+ {
+ // save the lump data
+ int offset = g_pBSPHeader->lumps[lump].fileofs;
+ SetAlignedLumpPosition( lump );
+ SafeWrite( g_hBSPFile, (byte *)g_pBSPHeader + offset, length );
+ }
+ else
+ {
+ g_pBSPHeader->lumps[lump].fileofs = 0;
+ }
+ }
+
+ // Always write the pak file at the end
+ // Pad out the end of the file to a sector boundary for optimal IO
+ g_pBSPHeader->lumps[LUMP_PAKFILE].fileofs = AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE );
+ g_pBSPHeader->lumps[LUMP_PAKFILE].filelen = pakSize;
+ SafeWrite( g_hBSPFile, pPakData, pakSize );
+
+ // Pad out the end of the file to a sector boundary for optimal IO
+ AlignFilePosition( g_hBSPFile, XBOX_DVD_SECTORSIZE );
+
+ // Write the updated header
+ g_pFileSystem->Seek( g_hBSPFile, 0, FILESYSTEM_SEEK_HEAD );
+ WriteData( g_pBSPHeader );
+ g_pFileSystem->Close( g_hBSPFile );
+
+ CloseBSPFile();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Build a list of files that BSP owns, world/cubemap materials, static props, etc.
+//-----------------------------------------------------------------------------
+bool GetBSPDependants( const char *pBSPFilename, CUtlVector< CUtlString > *pList )
+{
+ if ( !g_pFileSystem->FileExists( pBSPFilename ) )
+ {
+ Warning( "Error! Couldn't open file %s!\n", pBSPFilename );
+ return false;
+ }
+
+ // must be set, but exact hdr not critical for dependant traversal
+ SetHDRMode( false );
+
+ LoadBSPFile( pBSPFilename );
+
+ char szBspName[MAX_PATH];
+ V_FileBase( pBSPFilename, szBspName, sizeof( szBspName ) );
+ V_SetExtension( szBspName, ".bsp", sizeof( szBspName ) );
+
+ // get embedded pak files, and internals
+ char szFilename[MAX_PATH];
+ int fileSize;
+ int fileId = -1;
+ for ( ;; )
+ {
+ fileId = GetPakFile()->GetNextFilename( fileId, szFilename, sizeof( szFilename ), fileSize );
+ if ( fileId == -1 )
+ {
+ break;
+ }
+ pList->AddToTail( szFilename );
+ }
+
+ // get all the world materials
+ for ( int i=0; i<numtexdata; i++ )
+ {
+ const char *pName = TexDataStringTable_GetString( dtexdata[i].nameStringTableID );
+ V_ComposeFileName( "materials", pName, szFilename, sizeof( szFilename ) );
+ V_SetExtension( szFilename, ".vmt", sizeof( szFilename ) );
+ pList->AddToTail( szFilename );
+ }
+
+ // get all the static props
+ GameLumpHandle_t hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
+ if ( hGameLump != g_GameLumps.InvalidGameLump() )
+ {
+ byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump );
+ if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) )
+ {
+ int count = ((int *)pGameLumpData)[0];
+ pGameLumpData += sizeof( int );
+
+ StaticPropDictLump_t *pStaticPropDictLump = (StaticPropDictLump_t *)pGameLumpData;
+ for ( int i=0; i<count; i++ )
+ {
+ pList->AddToTail( pStaticPropDictLump[i].m_Name );
+ }
+ }
+ }
+
+ // get all the detail props
+ hGameLump = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS );
+ if ( hGameLump != g_GameLumps.InvalidGameLump() )
+ {
+ byte *pGameLumpData = (byte *)g_GameLumps.GetGameLump( hGameLump );
+ if ( pGameLumpData && g_GameLumps.GameLumpSize( hGameLump ) )
+ {
+ int count = ((int *)pGameLumpData)[0];
+ pGameLumpData += sizeof( int );
+
+ DetailObjectDictLump_t *pDetailObjectDictLump = (DetailObjectDictLump_t *)pGameLumpData;
+ for ( int i=0; i<count; i++ )
+ {
+ pList->AddToTail( pDetailObjectDictLump[i].m_Name );
+ }
+ pGameLumpData += count * sizeof( DetailObjectDictLump_t );
+
+ if ( g_GameLumps.GetGameLumpVersion( hGameLump ) == 4 )
+ {
+ count = ((int *)pGameLumpData)[0];
+ pGameLumpData += sizeof( int );
+ if ( count )
+ {
+ // All detail prop sprites must lie in the material detail/detailsprites
+ pList->AddToTail( "materials/detail/detailsprites.vmt" );
+ }
+ }
+ }
+ }
+
+ UnloadBSPFile();
+
+ return true;
+}
+
diff --git a/mp/src/utils/common/bsplib.h b/mp/src/utils/common/bsplib.h new file mode 100644 index 00000000..83486e8b --- /dev/null +++ b/mp/src/utils/common/bsplib.h @@ -0,0 +1,404 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BSPLIB_H
+#define BSPLIB_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "bspfile.h"
+#include "utlvector.h"
+#include "utlstring.h"
+#include "utllinkedlist.h"
+#include "byteswap.h"
+#ifdef ENGINE_DLL
+#include "zone.h"
+#endif
+
+#ifdef ENGINE_DLL
+typedef CUtlVector<unsigned char, CHunkMemory<unsigned char> > CDispLightmapSamplePositions;
+#else
+typedef CUtlVector<unsigned char> CDispLightmapSamplePositions;
+#endif
+
+class ISpatialQuery;
+struct Ray_t;
+class Vector2D;
+struct portal_t;
+class CUtlBuffer;
+class IZip;
+
+// this is only true in vrad
+extern bool g_bHDR;
+
+// default width/height of luxels in world units.
+#define DEFAULT_LUXEL_SIZE ( 16.0f )
+
+#define SINGLE_BRUSH_MAP (MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_BRUSH_LIGHTMAP_DIM_INCLUDING_BORDER)
+#define SINGLEMAP (MAX_LIGHTMAP_DIM_INCLUDING_BORDER*MAX_LIGHTMAP_DIM_INCLUDING_BORDER)
+
+struct entity_t
+{
+ Vector origin;
+ int firstbrush;
+ int numbrushes;
+ epair_t *epairs;
+
+ // only valid for func_areaportals
+ int areaportalnum;
+ int portalareas[2];
+ portal_t *m_pPortalsLeadingIntoAreas[2]; // portals leading into portalareas
+};
+
+extern int num_entities;
+extern entity_t entities[MAX_MAP_ENTITIES];
+
+extern int nummodels;
+extern dmodel_t dmodels[MAX_MAP_MODELS];
+
+extern int visdatasize;
+extern byte dvisdata[MAX_MAP_VISIBILITY];
+extern dvis_t *dvis;
+
+extern CUtlVector<byte> dlightdataHDR;
+extern CUtlVector<byte> dlightdataLDR;
+extern CUtlVector<byte> *pdlightdata;
+extern CUtlVector<char> dentdata;
+
+extern int numleafs;
+#if !defined( _X360 )
+extern dleaf_t dleafs[MAX_MAP_LEAFS];
+#else
+extern dleaf_t *dleafs;
+#endif
+extern CUtlVector<dleafambientlighting_t> *g_pLeafAmbientLighting;
+extern CUtlVector<dleafambientindex_t> *g_pLeafAmbientIndex;
+extern unsigned short g_LeafMinDistToWater[MAX_MAP_LEAFS];
+
+extern int numplanes;
+extern dplane_t dplanes[MAX_MAP_PLANES];
+
+extern int numvertexes;
+extern dvertex_t dvertexes[MAX_MAP_VERTS];
+
+extern int g_numvertnormalindices; // dfaces reference these. These index g_vertnormals.
+extern unsigned short g_vertnormalindices[MAX_MAP_VERTNORMALS];
+
+extern int g_numvertnormals;
+extern Vector g_vertnormals[MAX_MAP_VERTNORMALS];
+
+extern int numnodes;
+extern dnode_t dnodes[MAX_MAP_NODES];
+
+extern CUtlVector<texinfo_t> texinfo;
+
+extern int numtexdata;
+extern dtexdata_t dtexdata[MAX_MAP_TEXDATA];
+
+// displacement map .bsp file info
+extern CUtlVector<ddispinfo_t> g_dispinfo;
+extern CUtlVector<CDispVert> g_DispVerts;
+extern CUtlVector<CDispTri> g_DispTris;
+extern CDispLightmapSamplePositions g_DispLightmapSamplePositions; // LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS
+
+extern int numorigfaces;
+extern dface_t dorigfaces[MAX_MAP_FACES];
+
+extern int g_numprimitives;
+extern dprimitive_t g_primitives[MAX_MAP_PRIMITIVES];
+
+extern int g_numprimverts;
+extern dprimvert_t g_primverts[MAX_MAP_PRIMVERTS];
+
+extern int g_numprimindices;
+extern unsigned short g_primindices[MAX_MAP_PRIMINDICES];
+
+extern int numfaces;
+extern dface_t dfaces[MAX_MAP_FACES];
+
+extern int numfaceids;
+extern CUtlVector<dfaceid_t> dfaceids;
+
+extern int numfaces_hdr;
+extern dface_t dfaces_hdr[MAX_MAP_FACES];
+
+extern int numedges;
+extern dedge_t dedges[MAX_MAP_EDGES];
+
+extern int numleaffaces;
+extern unsigned short dleaffaces[MAX_MAP_LEAFFACES];
+
+extern int numleafbrushes;
+extern unsigned short dleafbrushes[MAX_MAP_LEAFBRUSHES];
+
+extern int numsurfedges;
+extern int dsurfedges[MAX_MAP_SURFEDGES];
+
+extern int numareas;
+extern darea_t dareas[MAX_MAP_AREAS];
+
+extern int numareaportals;
+extern dareaportal_t dareaportals[MAX_MAP_AREAPORTALS];
+
+extern int numbrushes;
+extern dbrush_t dbrushes[MAX_MAP_BRUSHES];
+
+extern int numbrushsides;
+extern dbrushside_t dbrushsides[MAX_MAP_BRUSHSIDES];
+
+extern int *pNumworldlights;
+extern dworldlight_t *dworldlights;
+
+extern Vector g_ClipPortalVerts[MAX_MAP_PORTALVERTS];
+extern int g_nClipPortalVerts;
+
+extern dcubemapsample_t g_CubemapSamples[MAX_MAP_CUBEMAPSAMPLES];
+extern int g_nCubemapSamples;
+
+extern int g_nOverlayCount;
+extern doverlay_t g_Overlays[MAX_MAP_OVERLAYS];
+extern doverlayfade_t g_OverlayFades[MAX_MAP_OVERLAYS]; // Parallel array of fade info in a separate lump to avoid breaking backwards compat
+
+extern int g_nWaterOverlayCount;
+extern dwateroverlay_t g_WaterOverlays[MAX_MAP_WATEROVERLAYS];
+
+extern CUtlVector<char> g_TexDataStringData;
+extern CUtlVector<int> g_TexDataStringTable;
+
+extern int numleafwaterdata;
+extern dleafwaterdata_t dleafwaterdata[MAX_MAP_LEAFWATERDATA];
+
+extern CUtlVector<CFaceMacroTextureInfo> g_FaceMacroTextureInfos;
+
+extern CUtlVector<doccluderdata_t> g_OccluderData;
+extern CUtlVector<doccluderpolydata_t> g_OccluderPolyData;
+extern CUtlVector<int> g_OccluderVertexIndices;
+
+// level flags - see LVLFLAGS_xxx in bspfile.h
+extern uint32 g_LevelFlags;
+
+// physics collision data
+extern byte *g_pPhysCollide;
+extern int g_PhysCollideSize;
+extern byte *g_pPhysDisp;
+extern int g_PhysDispSize;
+
+// Embedded pack/pak file
+IZip *GetPakFile( void );
+IZip *GetSwapPakFile( void );
+void ClearPakFile( IZip *pak );
+void AddFileToPak( IZip *pak, const char *pRelativeName, const char *fullpath );
+void AddBufferToPak( IZip *pak, const char *pRelativeName, void *data, int length, bool bTextMode );
+bool FileExistsInPak( IZip *pak, const char *pRelativeName );
+bool ReadFileFromPak( IZip *pak, const char *pRelativeName, bool bTextMode, CUtlBuffer &buf );
+void RemoveFileFromPak( IZip *pak, const char *pRelativeName );
+int GetNextFilename( IZip *pak, int id, char *pBuffer, int bufferSize, int &fileSize );
+void ForceAlignment( IZip *pak, bool bAlign, bool bCompatibleFormat, unsigned int alignmentSize );
+
+typedef bool (*CompressFunc_t)( CUtlBuffer &inputBuffer, CUtlBuffer &outputBuffer );
+typedef bool (*VTFConvertFunc_t)( const char *pDebugName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf, CompressFunc_t pCompressFunc );
+typedef bool (*VHVFixupFunc_t)( const char *pVhvFilename, const char *pModelName, CUtlBuffer &sourceBuf, CUtlBuffer &targetBuf );
+
+//-----------------------------------------------------------------------------
+// Game lump memory storage
+//-----------------------------------------------------------------------------
+// NOTE: This is not optimal at all; since I expect client lumps to
+// not be accessed all that often.
+
+struct GameLump_t
+{
+ GameLumpId_t m_Id;
+ unsigned short m_Flags;
+ unsigned short m_Version;
+ CUtlMemory< unsigned char > m_Memory;
+};
+
+//-----------------------------------------------------------------------------
+// Handle to a game lump
+//-----------------------------------------------------------------------------
+typedef unsigned short GameLumpHandle_t;
+
+class CGameLump
+{
+public:
+ //-----------------------------------------------------------------------------
+ // Convert four-CC code to a handle + back
+ //-----------------------------------------------------------------------------
+ GameLumpHandle_t GetGameLumpHandle( GameLumpId_t id );
+ GameLumpId_t GetGameLumpId( GameLumpHandle_t handle );
+ int GetGameLumpFlags( GameLumpHandle_t handle );
+ int GetGameLumpVersion( GameLumpHandle_t handle );
+ void ComputeGameLumpSizeAndCount( int& size, int& clumpCount );
+ void ParseGameLump( dheader_t* pHeader );
+ void SwapGameLump( GameLumpId_t id, int version, byte *dest, byte *src, int size );
+
+
+ //-----------------------------------------------------------------------------
+ // Game lump accessor methods
+ //-----------------------------------------------------------------------------
+ void* GetGameLump( GameLumpHandle_t handle );
+ int GameLumpSize( GameLumpHandle_t handle );
+
+
+ //-----------------------------------------------------------------------------
+ // Game lump iteration methods
+ //-----------------------------------------------------------------------------
+ GameLumpHandle_t FirstGameLump();
+ GameLumpHandle_t NextGameLump( GameLumpHandle_t handle );
+ GameLumpHandle_t InvalidGameLump();
+
+
+ //-----------------------------------------------------------------------------
+ // Game lump creation/destruction method
+ //-----------------------------------------------------------------------------
+ GameLumpHandle_t CreateGameLump( GameLumpId_t id, int size, int flags, int version );
+ void DestroyGameLump( GameLumpHandle_t handle );
+ void DestroyAllGameLumps();
+
+private:
+ CUtlLinkedList< GameLump_t, GameLumpHandle_t > m_GameLumps;
+};
+
+extern CGameLump g_GameLumps;
+extern CByteswap g_Swap;
+
+//-----------------------------------------------------------------------------
+// Helper for the bspzip tool
+//-----------------------------------------------------------------------------
+void ExtractZipFileFromBSP( char *pBSPFileName, char *pZipFileName );
+
+
+//-----------------------------------------------------------------------------
+// String table methods
+//-----------------------------------------------------------------------------
+const char * TexDataStringTable_GetString( int stringID );
+int TexDataStringTable_AddOrFindString( const char *pString );
+
+void DecompressVis (byte *in, byte *decompressed);
+int CompressVis (byte *vis, byte *dest);
+
+void OpenBSPFile( const char *filename );
+void CloseBSPFile(void);
+void LoadBSPFile( const char *filename );
+void LoadBSPFile_FileSystemOnly( const char *filename );
+void LoadBSPFileTexinfo( const char *filename );
+void WriteBSPFile( const char *filename, char *pUnused = NULL );
+void PrintBSPFileSizes(void);
+void PrintBSPPackDirectory(void);
+void ReleasePakFileLumps(void);
+bool SwapBSPFile( const char *filename, const char *swapFilename, bool bSwapOnLoad, VTFConvertFunc_t pVTFConvertFunc, VHVFixupFunc_t pVHVFixupFunc, CompressFunc_t pCompressFunc );
+bool GetPakFileLump( const char *pBSPFilename, void **pPakData, int *pPakSize );
+bool SetPakFileLump( const char *pBSPFilename, const char *pNewFilename, void *pPakData, int pakSize );
+void WriteLumpToFile( char *filename, int lump );
+void WriteLumpToFile( char *filename, int lump, int nLumpVersion, void *pBuffer, size_t nBufLen );
+bool GetBSPDependants( const char *pBSPFilename, CUtlVector< CUtlString > *pList );
+void UnloadBSPFile();
+
+void ParseEntities (void);
+void UnparseEntities (void);
+void PrintEntity (entity_t *ent);
+
+void SetKeyValue (entity_t *ent, const char *key, const char *value);
+char *ValueForKey (entity_t *ent, char *key);
+// will return "" if not present
+int IntForKey (entity_t *ent, char *key);
+int IntForKeyWithDefault(entity_t *ent, char *key, int nDefault );
+vec_t FloatForKey (entity_t *ent, char *key);
+vec_t FloatForKeyWithDefault (entity_t *ent, char *key, float default_value);
+void GetVectorForKey (entity_t *ent, char *key, Vector& vec);
+void GetVector2DForKey (entity_t *ent, char *key, Vector2D& vec);
+void GetAnglesForKey (entity_t *ent, char *key, QAngle& vec);
+epair_t *ParseEpair (void);
+void StripTrailing (char *e);
+
+// Build a list of the face's vertices (index into dvertexes).
+// points must be able to hold pFace->numedges indices.
+void BuildFaceCalcWindingData( dface_t *pFace, int *points );
+
+// Convert a tristrip to a trilist.
+// Removes degenerates.
+// Fills in pTriListIndices and pnTriListIndices.
+// You must free pTriListIndices with delete[].
+void TriStripToTriList(
+ unsigned short const *pTriStripIndices,
+ int nTriStripIndices,
+ unsigned short **pTriListIndices,
+ int *pnTriListIndices );
+
+// Calculates the lightmap coordinates at a given set of positions given the
+// lightmap basis information.
+void CalcTextureCoordsAtPoints(
+ float const texelsPerWorldUnits[2][4],
+ int const subtractOffset[2],
+ Vector const *pPoints,
+ int const nPoints,
+ Vector2D *pCoords );
+
+// Figure out lightmap extents on all (lit) faces.
+void UpdateAllFaceLightmapExtents();
+
+
+//-----------------------------------------------------------------------------
+// Gets at an interface for the tree for enumeration of leaves in volumes.
+//-----------------------------------------------------------------------------
+ISpatialQuery* ToolBSPTree();
+
+class IBSPNodeEnumerator
+{
+public:
+ // call back with a node and a context
+ virtual bool EnumerateNode( int node, Ray_t const& ray, float f, int context ) = 0;
+
+ // call back with a leaf and a context
+ virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context ) = 0;
+};
+
+//-----------------------------------------------------------------------------
+// Enumerates nodes + leafs in front to back order...
+//-----------------------------------------------------------------------------
+bool EnumerateNodesAlongRay( Ray_t const& ray, IBSPNodeEnumerator* pEnum, int context );
+
+
+//-----------------------------------------------------------------------------
+// Helps us find all leaves associated with a particular cluster
+//-----------------------------------------------------------------------------
+struct clusterlist_t
+{
+ int leafCount;
+ CUtlVector<int> leafs;
+};
+
+extern CUtlVector<clusterlist_t> g_ClusterLeaves;
+
+// Call this to build the mapping from cluster to leaves
+void BuildClusterTable( );
+
+void GetPlatformMapPath( const char *pMapPath, char *pPlatformMapPath, int dxlevel, int maxLength );
+
+void SetHDRMode( bool bHDR );
+
+// ----------------------------------------------------------------------------- //
+// Helper accessors for the various structures.
+// ----------------------------------------------------------------------------- //
+
+inline ColorRGBExp32* dface_AvgLightColor( dface_t *pFace, int nLightStyleIndex )
+{
+ return (ColorRGBExp32*)&(*pdlightdata)[pFace->lightofs - (nLightStyleIndex+1) * 4];
+}
+
+inline const char* TexInfo_TexName( int iTexInfo )
+{
+ return TexDataStringTable_GetString( dtexdata[texinfo[iTexInfo].texdata].nameStringTableID );
+}
+
+
+#endif // BSPLIB_H
diff --git a/mp/src/utils/common/cmdlib.cpp b/mp/src/utils/common/cmdlib.cpp new file mode 100644 index 00000000..a6962380 --- /dev/null +++ b/mp/src/utils/common/cmdlib.cpp @@ -0,0 +1,1007 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// -----------------------
+// cmdlib.c
+// -----------------------
+#include "tier0/platform.h"
+#ifdef IS_WINDOWS_PC
+#include <windows.h>
+#endif
+#include "cmdlib.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "tier1/strtools.h"
+#ifdef _WIN32
+#include <conio.h>
+#endif
+#include "utlvector.h"
+#include "filesystem_helpers.h"
+#include "utllinkedlist.h"
+#include "tier0/icommandline.h"
+#include "KeyValues.h"
+#include "filesystem_tools.h"
+
+#if defined( MPI )
+
+ #include "vmpi.h"
+ #include "vmpi_tools_shared.h"
+
+#endif
+
+
+#if defined( _WIN32 ) || defined( WIN32 )
+#include <direct.h>
+#endif
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+
+// set these before calling CheckParm
+int myargc;
+char **myargv;
+
+char com_token[1024];
+
+qboolean archive;
+char archivedir[1024];
+
+FileHandle_t g_pLogFile = 0;
+
+CUtlLinkedList<CleanupFn, unsigned short> g_CleanupFunctions;
+CUtlLinkedList<SpewHookFn, unsigned short> g_ExtraSpewHooks;
+
+bool g_bStopOnExit = false;
+void (*g_ExtraSpewHook)(const char*) = NULL;
+
+#if defined( _WIN32 ) || defined( WIN32 )
+
+void CmdLib_FPrintf( FileHandle_t hFile, const char *pFormat, ... )
+{
+ static CUtlVector<char> buf;
+ if ( buf.Count() == 0 )
+ buf.SetCount( 1024 );
+
+ va_list marker;
+ va_start( marker, pFormat );
+
+ while ( 1 )
+ {
+ int ret = Q_vsnprintf( buf.Base(), buf.Count(), pFormat, marker );
+ if ( ret >= 0 )
+ {
+ // Write the string.
+ g_pFileSystem->Write( buf.Base(), ret, hFile );
+
+ break;
+ }
+ else
+ {
+ // Make the buffer larger.
+ int newSize = buf.Count() * 2;
+ buf.SetCount( newSize );
+ if ( buf.Count() != newSize )
+ {
+ Error( "CmdLib_FPrintf: can't allocate space for text." );
+ }
+ }
+ }
+
+ va_end( marker );
+}
+
+char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile )
+{
+ int iCur=0;
+ for ( ; iCur < (outSize-1); iCur++ )
+ {
+ char c;
+ if ( !g_pFileSystem->Read( &c, 1, hFile ) )
+ {
+ if ( iCur == 0 )
+ return NULL;
+ else
+ break;
+ }
+
+ pOut[iCur] = c;
+ if ( c == '\n' )
+ break;
+
+ if ( c == EOF )
+ {
+ if ( iCur == 0 )
+ return NULL;
+ else
+ break;
+ }
+ }
+
+ pOut[iCur] = 0;
+ return pOut;
+}
+
+#if !defined( _X360 )
+#include <wincon.h>
+#endif
+
+// This pauses before exiting if they use -StopOnExit. Useful for debugging.
+class CExitStopper
+{
+public:
+ ~CExitStopper()
+ {
+ if ( g_bStopOnExit )
+ {
+ Warning( "\nPress any key to quit.\n" );
+ getch();
+ }
+ }
+} g_ExitStopper;
+
+
+static unsigned short g_InitialColor = 0xFFFF;
+static unsigned short g_LastColor = 0xFFFF;
+static unsigned short g_BadColor = 0xFFFF;
+static WORD g_BackgroundFlags = 0xFFFF;
+static void GetInitialColors( )
+{
+#if !defined( _X360 )
+ // Get the old background attributes.
+ CONSOLE_SCREEN_BUFFER_INFO oldInfo;
+ GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), &oldInfo );
+ g_InitialColor = g_LastColor = oldInfo.wAttributes & (FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY);
+ g_BackgroundFlags = oldInfo.wAttributes & (BACKGROUND_RED|BACKGROUND_GREEN|BACKGROUND_BLUE|BACKGROUND_INTENSITY);
+
+ g_BadColor = 0;
+ if (g_BackgroundFlags & BACKGROUND_RED)
+ g_BadColor |= FOREGROUND_RED;
+ if (g_BackgroundFlags & BACKGROUND_GREEN)
+ g_BadColor |= FOREGROUND_GREEN;
+ if (g_BackgroundFlags & BACKGROUND_BLUE)
+ g_BadColor |= FOREGROUND_BLUE;
+ if (g_BackgroundFlags & BACKGROUND_INTENSITY)
+ g_BadColor |= FOREGROUND_INTENSITY;
+#endif
+}
+
+WORD SetConsoleTextColor( int red, int green, int blue, int intensity )
+{
+ WORD ret = g_LastColor;
+#if !defined( _X360 )
+
+ g_LastColor = 0;
+ if( red ) g_LastColor |= FOREGROUND_RED;
+ if( green ) g_LastColor |= FOREGROUND_GREEN;
+ if( blue ) g_LastColor |= FOREGROUND_BLUE;
+ if( intensity ) g_LastColor |= FOREGROUND_INTENSITY;
+
+ // Just use the initial color if there's a match...
+ if (g_LastColor == g_BadColor)
+ g_LastColor = g_InitialColor;
+
+ SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), g_LastColor | g_BackgroundFlags );
+#endif
+ return ret;
+}
+
+void RestoreConsoleTextColor( WORD color )
+{
+#if !defined( _X360 )
+ SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), color | g_BackgroundFlags );
+ g_LastColor = color;
+#endif
+}
+
+
+#if defined( CMDLIB_NODBGLIB )
+
+// This can go away when everything is in bin.
+void Error( char const *pMsg, ... )
+{
+ va_list marker;
+ va_start( marker, pMsg );
+ vprintf( pMsg, marker );
+ va_end( marker );
+
+ exit( -1 );
+}
+
+#else
+
+CRITICAL_SECTION g_SpewCS;
+bool g_bSpewCSInitted = false;
+bool g_bSuppressPrintfOutput = false;
+
+SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg )
+{
+ // Hopefully two threads won't call this simultaneously right at the start!
+ if ( !g_bSpewCSInitted )
+ {
+ InitializeCriticalSection( &g_SpewCS );
+ g_bSpewCSInitted = true;
+ }
+
+ WORD old;
+ SpewRetval_t retVal;
+
+ EnterCriticalSection( &g_SpewCS );
+ {
+ if (( type == SPEW_MESSAGE ) || (type == SPEW_LOG ))
+ {
+ Color c = *GetSpewOutputColor();
+ if ( c.r() != 255 || c.g() != 255 || c.b() != 255 )
+ {
+ // custom color
+ old = SetConsoleTextColor( c.r(), c.g(), c.b(), c.a() );
+ }
+ else
+ {
+ old = SetConsoleTextColor( 1, 1, 1, 0 );
+ }
+ retVal = SPEW_CONTINUE;
+ }
+ else if( type == SPEW_WARNING )
+ {
+ old = SetConsoleTextColor( 1, 1, 0, 1 );
+ retVal = SPEW_CONTINUE;
+ }
+ else if( type == SPEW_ASSERT )
+ {
+ old = SetConsoleTextColor( 1, 0, 0, 1 );
+ retVal = SPEW_DEBUGGER;
+
+#ifdef MPI
+ // VMPI workers don't want to bring up dialogs and suchlike.
+ // They need to have a special function installed to handle
+ // the exceptions and write the minidumps.
+ // Install the function after VMPI_Init with a call:
+ // SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
+ if ( g_bUseMPI && !g_bMPIMaster && !Plat_IsInDebugSession() )
+ {
+ // Generating an exception and letting the
+ // installed handler handle it
+ ::RaiseException
+ (
+ 0, // dwExceptionCode
+ EXCEPTION_NONCONTINUABLE, // dwExceptionFlags
+ 0, // nNumberOfArguments,
+ NULL // const ULONG_PTR* lpArguments
+ );
+
+ // Never get here (non-continuable exception)
+
+ VMPI_HandleCrash( pMsg, NULL, true );
+ exit( 0 );
+ }
+#endif
+ }
+ else if( type == SPEW_ERROR )
+ {
+ old = SetConsoleTextColor( 1, 0, 0, 1 );
+ retVal = SPEW_ABORT; // doesn't matter.. we exit below so we can return an errorlevel (which dbg.dll doesn't do).
+ }
+ else
+ {
+ old = SetConsoleTextColor( 1, 1, 1, 1 );
+ retVal = SPEW_CONTINUE;
+ }
+
+ if ( !g_bSuppressPrintfOutput || type == SPEW_ERROR )
+ printf( "%s", pMsg );
+
+ OutputDebugString( pMsg );
+
+ if ( type == SPEW_ERROR )
+ {
+ printf( "\n" );
+ OutputDebugString( "\n" );
+ }
+
+ if( g_pLogFile )
+ {
+ CmdLib_FPrintf( g_pLogFile, "%s", pMsg );
+ g_pFileSystem->Flush( g_pLogFile );
+ }
+
+ // Dispatch to other spew hooks.
+ FOR_EACH_LL( g_ExtraSpewHooks, i )
+ g_ExtraSpewHooks[i]( pMsg );
+
+ RestoreConsoleTextColor( old );
+ }
+ LeaveCriticalSection( &g_SpewCS );
+
+ if ( type == SPEW_ERROR )
+ {
+ CmdLib_Exit( 1 );
+ }
+
+ return retVal;
+}
+
+
+void InstallSpewFunction()
+{
+ setvbuf( stdout, NULL, _IONBF, 0 );
+ setvbuf( stderr, NULL, _IONBF, 0 );
+
+ SpewOutputFunc( CmdLib_SpewOutputFunc );
+ GetInitialColors();
+}
+
+
+void InstallExtraSpewHook( SpewHookFn pFn )
+{
+ g_ExtraSpewHooks.AddToTail( pFn );
+}
+
+#if 0
+void CmdLib_AllocError( unsigned long size )
+{
+ Error( "Error trying to allocate %d bytes.\n", size );
+}
+
+
+int CmdLib_NewHandler( size_t size )
+{
+ CmdLib_AllocError( size );
+ return 0;
+}
+#endif
+
+void InstallAllocationFunctions()
+{
+// _set_new_mode( 1 ); // so if malloc() fails, we exit.
+// _set_new_handler( CmdLib_NewHandler );
+}
+
+void SetSpewFunctionLogFile( char const *pFilename )
+{
+ Assert( (!g_pLogFile) );
+ g_pLogFile = g_pFileSystem->Open( pFilename, "a" );
+
+ Assert( g_pLogFile );
+ if (!g_pLogFile)
+ Error("Can't create LogFile:\"%s\"\n", pFilename );
+
+ CmdLib_FPrintf( g_pLogFile, "\n\n\n" );
+}
+
+
+void CloseSpewFunctionLogFile()
+{
+ if ( g_pFileSystem && g_pLogFile )
+ {
+ g_pFileSystem->Close( g_pLogFile );
+ g_pLogFile = FILESYSTEM_INVALID_HANDLE;
+ }
+}
+
+
+void CmdLib_AtCleanup( CleanupFn pFn )
+{
+ g_CleanupFunctions.AddToTail( pFn );
+}
+
+
+void CmdLib_Cleanup()
+{
+ CloseSpewFunctionLogFile();
+
+ CmdLib_TermFileSystem();
+
+ FOR_EACH_LL( g_CleanupFunctions, i )
+ g_CleanupFunctions[i]();
+
+#if defined( MPI )
+ // Unfortunately, when you call exit(), even if you have things registered with atexit(),
+ // threads go into a seemingly undefined state where GetExitCodeThread gives STILL_ACTIVE
+ // and WaitForSingleObject will stall forever on the thread. Because of this, we must cleanup
+ // everything that uses threads before exiting.
+ VMPI_Finalize();
+#endif
+}
+
+
+void CmdLib_Exit( int exitCode )
+{
+ TerminateProcess( GetCurrentProcess(), 1 );
+}
+
+
+
+#endif
+
+#endif
+
+
+
+
+/*
+===================
+ExpandWildcards
+
+Mimic unix command line expansion
+===================
+*/
+#define MAX_EX_ARGC 1024
+int ex_argc;
+char *ex_argv[MAX_EX_ARGC];
+#if defined( _WIN32 ) && !defined( _X360 )
+#include "io.h"
+void ExpandWildcards (int *argc, char ***argv)
+{
+ struct _finddata_t fileinfo;
+ int handle;
+ int i;
+ char filename[1024];
+ char filebase[1024];
+ char *path;
+
+ ex_argc = 0;
+ for (i=0 ; i<*argc ; i++)
+ {
+ path = (*argv)[i];
+ if ( path[0] == '-'
+ || ( !strstr(path, "*") && !strstr(path, "?") ) )
+ {
+ ex_argv[ex_argc++] = path;
+ continue;
+ }
+
+ handle = _findfirst (path, &fileinfo);
+ if (handle == -1)
+ return;
+
+ Q_ExtractFilePath (path, filebase, sizeof( filebase ));
+
+ do
+ {
+ sprintf (filename, "%s%s", filebase, fileinfo.name);
+ ex_argv[ex_argc++] = copystring (filename);
+ } while (_findnext( handle, &fileinfo ) != -1);
+
+ _findclose (handle);
+ }
+
+ *argc = ex_argc;
+ *argv = ex_argv;
+}
+#else
+void ExpandWildcards (int *argc, char ***argv)
+{
+}
+#endif
+
+
+// only printf if in verbose mode
+qboolean verbose = false;
+void qprintf (const char *format, ...)
+{
+ if (!verbose)
+ return;
+
+ va_list argptr;
+ va_start (argptr,format);
+
+ char str[2048];
+ Q_vsnprintf( str, sizeof(str), format, argptr );
+
+#if defined( CMDLIB_NODBGLIB )
+ printf( "%s", str );
+#else
+ Msg( "%s", str );
+#endif
+
+ va_end (argptr);
+}
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// Helpers.
+// ---------------------------------------------------------------------------------------------------- //
+
+static void CmdLib_getwd( char *out, int outSize )
+{
+#if defined( _WIN32 ) || defined( WIN32 )
+ _getcwd( out, outSize );
+ Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS );
+#else
+ getcwd(out, outSize);
+ strcat(out, "/");
+#endif
+ Q_FixSlashes( out );
+}
+
+char *ExpandArg (char *path)
+{
+ static char full[1024];
+
+ if (path[0] != '/' && path[0] != '\\' && path[1] != ':')
+ {
+ CmdLib_getwd (full, sizeof( full ));
+ Q_strncat (full, path, sizeof( full ), COPY_ALL_CHARACTERS);
+ }
+ else
+ Q_strncpy (full, path, sizeof( full ));
+ return full;
+}
+
+
+char *ExpandPath (char *path)
+{
+ static char full[1024];
+ if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
+ return path;
+ sprintf (full, "%s%s", qdir, path);
+ return full;
+}
+
+
+
+char *copystring(const char *s)
+{
+ char *b;
+ b = (char *)malloc(strlen(s)+1);
+ strcpy (b, s);
+ return b;
+}
+
+
+void GetHourMinuteSeconds( int nInputSeconds, int &nHours, int &nMinutes, int &nSeconds )
+{
+}
+
+
+void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen )
+{
+ int nMinutes = nInputSeconds / 60;
+ int nSeconds = nInputSeconds - nMinutes * 60;
+ int nHours = nMinutes / 60;
+ nMinutes -= nHours * 60;
+
+ const char *extra[2] = { "", "s" };
+
+ if ( nHours > 0 )
+ Q_snprintf( pOut, outLen, "%d hour%s, %d minute%s, %d second%s", nHours, extra[nHours != 1], nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] );
+ else if ( nMinutes > 0 )
+ Q_snprintf( pOut, outLen, "%d minute%s, %d second%s", nMinutes, extra[nMinutes != 1], nSeconds, extra[nSeconds != 1] );
+ else
+ Q_snprintf( pOut, outLen, "%d second%s", nSeconds, extra[nSeconds != 1] );
+}
+
+
+void Q_mkdir (char *path)
+{
+#if defined( _WIN32 ) || defined( WIN32 )
+ if (_mkdir (path) != -1)
+ return;
+#else
+ if (mkdir (path, 0777) != -1)
+ return;
+#endif
+// if (errno != EEXIST)
+ Error ("mkdir failed %s\n", path );
+}
+
+void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage )
+{
+ FileSystem_Init( pFilename, maxMemoryUsage );
+ if ( !g_pFileSystem )
+ Error( "CmdLib_InitFileSystem failed." );
+}
+
+void CmdLib_TermFileSystem()
+{
+ FileSystem_Term();
+}
+
+CreateInterfaceFn CmdLib_GetFileSystemFactory()
+{
+ return FileSystem_GetFactory();
+}
+
+
+/*
+============
+FileTime
+
+returns -1 if not present
+============
+*/
+int FileTime (char *path)
+{
+ struct stat buf;
+
+ if (stat (path,&buf) == -1)
+ return -1;
+
+ return buf.st_mtime;
+}
+
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+ return (char*)ParseFile( data, com_token, NULL );
+}
+
+
+/*
+=============================================================================
+
+ MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+ int i;
+
+ for (i = 1;i<myargc;i++)
+ {
+ if ( !Q_strcasecmp(check, myargv[i]) )
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+/*
+================
+Q_filelength
+================
+*/
+int Q_filelength (FileHandle_t f)
+{
+ return g_pFileSystem->Size( f );
+}
+
+
+FileHandle_t SafeOpenWrite ( const char *filename )
+{
+ FileHandle_t f = g_pFileSystem->Open(filename, "wb");
+
+ if (!f)
+ {
+ //Error ("Error opening %s: %s",filename,strerror(errno));
+ // BUGBUG: No way to get equivalent of errno from IFileSystem!
+ Error ("Error opening %s! (Check for write enable)\n",filename);
+ }
+
+ return f;
+}
+
+#define MAX_CMDLIB_BASE_PATHS 10
+static char g_pBasePaths[MAX_CMDLIB_BASE_PATHS][MAX_PATH];
+static int g_NumBasePaths = 0;
+
+void CmdLib_AddBasePath( const char *pPath )
+{
+// printf( "CmdLib_AddBasePath( \"%s\" )\n", pPath );
+ if( g_NumBasePaths < MAX_CMDLIB_BASE_PATHS )
+ {
+ Q_strncpy( g_pBasePaths[g_NumBasePaths], pPath, MAX_PATH );
+ Q_FixSlashes( g_pBasePaths[g_NumBasePaths] );
+ g_NumBasePaths++;
+ }
+ else
+ {
+ Assert( 0 );
+ }
+}
+
+bool CmdLib_HasBasePath( const char *pFileName_, int &pathLength )
+{
+ char *pFileName = ( char * )_alloca( strlen( pFileName_ ) + 1 );
+ strcpy( pFileName, pFileName_ );
+ Q_FixSlashes( pFileName );
+ pathLength = 0;
+ int i;
+ for( i = 0; i < g_NumBasePaths; i++ )
+ {
+ // see if we can rip the base off of the filename.
+ if( Q_strncasecmp( g_pBasePaths[i], pFileName, strlen( g_pBasePaths[i] ) ) == 0 )
+ {
+ pathLength = strlen( g_pBasePaths[i] );
+ return true;
+ }
+ }
+ return false;
+}
+
+int CmdLib_GetNumBasePaths( void )
+{
+ return g_NumBasePaths;
+}
+
+const char *CmdLib_GetBasePath( int i )
+{
+ Assert( i >= 0 && i < g_NumBasePaths );
+ return g_pBasePaths[i];
+}
+
+
+//-----------------------------------------------------------------------------
+// Like ExpandPath but expands the path for each base path like SafeOpenRead
+//-----------------------------------------------------------------------------
+int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath )
+{
+ int nPathLength = 0;
+
+ pszPath = ExpandPath( const_cast< char * >( pszPath ) ); // Kind of redundant but it's how CmdLib_HasBasePath needs things
+
+ if ( CmdLib_HasBasePath( pszPath, nPathLength ) )
+ {
+ pszPath = pszPath + nPathLength;
+ for ( int i = 0; i < CmdLib_GetNumBasePaths(); ++i )
+ {
+ CUtlString &expandedPath = expandedPathList[ expandedPathList.AddToTail( CmdLib_GetBasePath( i ) ) ];
+ expandedPath += pszPath;
+ }
+ }
+ else
+ {
+ expandedPathList.AddToTail( pszPath );
+ }
+
+ return expandedPathList.Count();
+}
+
+
+FileHandle_t SafeOpenRead( const char *filename )
+{
+ int pathLength;
+ FileHandle_t f = 0;
+ if( CmdLib_HasBasePath( filename, pathLength ) )
+ {
+ filename = filename + pathLength;
+ int i;
+ for( i = 0; i < g_NumBasePaths; i++ )
+ {
+ char tmp[MAX_PATH];
+ strcpy( tmp, g_pBasePaths[i] );
+ strcat( tmp, filename );
+ f = g_pFileSystem->Open( tmp, "rb" );
+ if( f )
+ {
+ return f;
+ }
+ }
+ Error ("Error opening %s\n",filename );
+ return f;
+ }
+ else
+ {
+ f = g_pFileSystem->Open( filename, "rb" );
+ if ( !f )
+ Error ("Error opening %s",filename );
+
+ return f;
+ }
+}
+
+void SafeRead( FileHandle_t f, void *buffer, int count)
+{
+ if ( g_pFileSystem->Read (buffer, count, f) != (size_t)count)
+ Error ("File read failure");
+}
+
+
+void SafeWrite ( FileHandle_t f, void *buffer, int count)
+{
+ if (g_pFileSystem->Write (buffer, count, f) != (size_t)count)
+ Error ("File write failure");
+}
+
+
+/*
+==============
+FileExists
+==============
+*/
+qboolean FileExists ( const char *filename )
+{
+ FileHandle_t hFile = g_pFileSystem->Open( filename, "rb" );
+ if ( hFile == FILESYSTEM_INVALID_HANDLE )
+ {
+ return false;
+ }
+ else
+ {
+ g_pFileSystem->Close( hFile );
+ return true;
+ }
+}
+
+/*
+==============
+LoadFile
+==============
+*/
+int LoadFile ( const char *filename, void **bufferptr )
+{
+ int length = 0;
+ void *buffer;
+
+ FileHandle_t f = SafeOpenRead (filename);
+ if ( FILESYSTEM_INVALID_HANDLE != f )
+ {
+ length = Q_filelength (f);
+ buffer = malloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ g_pFileSystem->Close (f);
+ *bufferptr = buffer;
+ }
+ else
+ {
+ *bufferptr = NULL;
+ }
+ return length;
+}
+
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void SaveFile ( const char *filename, void *buffer, int count )
+{
+ FileHandle_t f = SafeOpenWrite (filename);
+ SafeWrite (f, buffer, count);
+ g_pFileSystem->Close (f);
+}
+
+/*
+====================
+Extract file parts
+====================
+*/
+// FIXME: should include the slash, otherwise
+// backing to an empty path will be wrong when appending a slash
+
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+ char *str;
+ int num;
+
+ num = 0;
+ str = hex;
+
+ while (*str)
+ {
+ num <<= 4;
+ if (*str >= '0' && *str <= '9')
+ num += *str-'0';
+ else if (*str >= 'a' && *str <= 'f')
+ num += 10 + *str-'a';
+ else if (*str >= 'A' && *str <= 'F')
+ num += 10 + *str-'A';
+ else
+ Error ("Bad hex number: %s",hex);
+ str++;
+ }
+
+ return num;
+}
+
+
+int ParseNum (char *str)
+{
+ if (str[0] == '$')
+ return ParseHex (str+1);
+ if (str[0] == '0' && str[1] == 'x')
+ return ParseHex (str+2);
+ return atol (str);
+}
+
+/*
+============
+CreatePath
+============
+*/
+void CreatePath (char *path)
+{
+ char *ofs, c;
+
+ // strip the drive
+ if (path[1] == ':')
+ path += 2;
+
+ for (ofs = path+1 ; *ofs ; ofs++)
+ {
+ c = *ofs;
+ if (c == '/' || c == '\\')
+ { // create the directory
+ *ofs = 0;
+ Q_mkdir (path);
+ *ofs = c;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Creates a path, path may already exist
+//-----------------------------------------------------------------------------
+#if defined( _WIN32 ) || defined( WIN32 )
+void SafeCreatePath( char *path )
+{
+ char *ptr;
+
+ // skip past the drive path, but don't strip
+ if ( path[1] == ':' )
+ {
+ ptr = strchr( path, '\\' );
+ }
+ else
+ {
+ ptr = path;
+ }
+ while ( ptr )
+ {
+ ptr = strchr( ptr+1, '\\' );
+ if ( ptr )
+ {
+ *ptr = '\0';
+ _mkdir( path );
+ *ptr = '\\';
+ }
+ }
+}
+#endif
+
+/*
+============
+QCopyFile
+
+ Used to archive source files
+============
+*/
+void QCopyFile (char *from, char *to)
+{
+ void *buffer;
+ int length;
+
+ length = LoadFile (from, &buffer);
+ CreatePath (to);
+ SaveFile (to, buffer, length);
+ free (buffer);
+}
+
+
+
diff --git a/mp/src/utils/common/cmdlib.h b/mp/src/utils/common/cmdlib.h new file mode 100644 index 00000000..50fa9d20 --- /dev/null +++ b/mp/src/utils/common/cmdlib.h @@ -0,0 +1,178 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CMDLIB_H
+#define CMDLIB_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+// cmdlib.h
+
+#include "basetypes.h"
+
+// This can go away when everything is in bin.
+#if defined( CMDLIB_NODBGLIB )
+ void Error( PRINTF_FORMAT_STRING char const *pMsg, ... );
+#else
+ #include "tier0/dbg.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+#include "filesystem.h"
+#include "filesystem_tools.h"
+#include "tier1/utlstring.h"
+
+
+// Tools should use this as the read path ID. It'll look into the paths specified by gameinfo.txt
+#define TOOLS_READ_PATH_ID "GAME"
+
+
+// Tools should use this to fprintf data to files.
+void CmdLib_FPrintf( FileHandle_t hFile, PRINTF_FORMAT_STRING const char *pFormat, ... );
+char* CmdLib_FGets( char *pOut, int outSize, FileHandle_t hFile );
+
+
+// This can be set so Msg() sends output to hook functions (like the VMPI MySQL database),
+// but doesn't actually printf the output.
+extern bool g_bSuppressPrintfOutput;
+
+extern IBaseFileSystem *g_pFileSystem;
+
+// These call right into the functions in filesystem_tools.h
+void CmdLib_InitFileSystem( const char *pFilename, int maxMemoryUsage = 0 );
+void CmdLib_TermFileSystem(); // GracefulExit calls this.
+CreateInterfaceFn CmdLib_GetFileSystemFactory();
+
+
+#ifdef _WIN32
+#pragma warning(disable : 4244) // MIPS
+#pragma warning(disable : 4136) // X86
+#pragma warning(disable : 4051) // ALPHA
+
+#pragma warning(disable : 4018) // signed/unsigned mismatch
+#pragma warning(disable : 4305) // truncate from double to float
+
+#pragma warning(disable : 4389) // singned/unsigned mismatch in ==
+#pragma warning(disable: 4512) // assignment operator could not be generated
+#endif
+
+
+// the dec offsetof macro doesnt work very well...
+#define myoffsetof(type,identifier) offsetof( type, identifier )
+
+
+// set these before calling CheckParm
+extern int myargc;
+extern char **myargv;
+
+int Q_filelength (FileHandle_t f);
+int FileTime (char *path);
+
+void Q_mkdir( char *path );
+
+char *ExpandArg (char *path); // expand relative to CWD
+char *ExpandPath (char *path); // expand relative to gamedir
+
+char *ExpandPathAndArchive (char *path);
+
+// Fills in pOut with "X hours, Y minutes, Z seconds". Leaves out hours or minutes if they're zero.
+void GetHourMinuteSecondsString( int nInputSeconds, char *pOut, int outLen );
+
+
+
+int CheckParm (char *check);
+
+FileHandle_t SafeOpenWrite ( const char *filename );
+FileHandle_t SafeOpenRead ( const char *filename );
+void SafeRead( FileHandle_t f, void *buffer, int count);
+void SafeWrite( FileHandle_t f, void *buffer, int count);
+
+int LoadFile ( const char *filename, void **bufferptr );
+void SaveFile ( const char *filename, void *buffer, int count );
+qboolean FileExists ( const char *filename );
+
+int ParseNum (char *str);
+
+// Do a printf in the specified color.
+#define CP_ERROR stderr, 1, 0, 0, 1 // default colors..
+#define CP_WARNING stderr, 1, 1, 0, 1
+#define CP_STARTUP stdout, 0, 1, 1, 1
+#define CP_NOTIFY stdout, 1, 1, 1, 1
+void ColorPrintf( FILE *pFile, bool red, bool green, bool blue, bool intensity, PRINTF_FORMAT_STRING char const *pFormat, ... );
+
+// Initialize spew output.
+void InstallSpewFunction();
+
+// This registers an extra callback for spew output.
+typedef void (*SpewHookFn)( const char * );
+void InstallExtraSpewHook( SpewHookFn pFn );
+
+// Install allocation hooks so we error out if an allocation can't happen.
+void InstallAllocationFunctions();
+
+// This shuts down mgrs that use threads gracefully. If you just call exit(), the threads can
+// get in a state where you can't tell if they are shutdown or not, and it can stall forever.
+typedef void (*CleanupFn)();
+void CmdLib_AtCleanup( CleanupFn pFn ); // register a callback when Cleanup() is called.
+void CmdLib_Cleanup();
+void CmdLib_Exit( int exitCode ); // Use this to cleanup and call exit().
+
+// entrypoint if chaining spew functions
+SpewRetval_t CmdLib_SpewOutputFunc( SpewType_t type, char const *pMsg );
+unsigned short SetConsoleTextColor( int red, int green, int blue, int intensity );
+void RestoreConsoleTextColor( unsigned short color );
+
+// Append all spew output to the specified file.
+void SetSpewFunctionLogFile( char const *pFilename );
+
+char *COM_Parse (char *data);
+
+extern char com_token[1024];
+
+char *copystring(const char *s);
+
+void CreatePath( char *path );
+void QCopyFile( char *from, char *to );
+void SafeCreatePath( char *path );
+
+extern qboolean archive;
+extern char archivedir[1024];
+
+extern qboolean verbose;
+
+void qprintf( PRINTF_FORMAT_STRING const char *format, ... );
+
+void ExpandWildcards (int *argc, char ***argv);
+
+void CmdLib_AddBasePath( const char *pBasePath );
+bool CmdLib_HasBasePath( const char *pFileName, int &pathLength );
+int CmdLib_GetNumBasePaths( void );
+const char *CmdLib_GetBasePath( int i );
+// Like ExpandPath but expands the path for each base path like SafeOpenRead
+int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath );
+
+extern bool g_bStopOnExit;
+
+// for compression routines
+typedef struct
+{
+ byte *data;
+ int count;
+} cblock_t;
+
+
+#endif // CMDLIB_H
\ No newline at end of file diff --git a/mp/src/utils/common/consolewnd.cpp b/mp/src/utils/common/consolewnd.cpp new file mode 100644 index 00000000..8802e39e --- /dev/null +++ b/mp/src/utils/common/consolewnd.cpp @@ -0,0 +1,333 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <windows.h>
+#include "consolewnd.h"
+
+
+#pragma warning( disable : 4311 ) // warning C4311: 'reinterpret_cast' : pointer truncation from 'CConsoleWnd *const ' to 'LONG'
+#pragma warning( disable : 4312 ) // warning C4312: 'type cast' : conversion from 'LONG' to 'CConsoleWnd *' of greater size
+
+#define EDITCONTROL_BORDER_SIZE 5
+
+
+// ------------------------------------------------------------------------------------------------ //
+// Functions to manage the console window.
+// ------------------------------------------------------------------------------------------------ //
+
+class CConsoleWnd : public IConsoleWnd
+{
+public:
+ CConsoleWnd();
+ ~CConsoleWnd();
+
+ bool Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible );
+ void Term();
+
+ virtual void Release();
+
+ virtual void SetVisible( bool bVisible );
+ virtual bool IsVisible() const;
+
+ virtual void PrintToConsole( const char *pMsg );
+ virtual void SetTitle( const char *pTitle );
+
+ virtual void SetDeleteOnClose( bool bDelete );
+
+
+private:
+
+ int WindowProc(
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ );
+
+ static int CALLBACK StaticWindowProc(
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ );
+
+ void RepositionEditControl();
+
+
+private:
+
+ HWND m_hWnd;
+ HWND m_hEditControl;
+ bool m_bVisible;
+ bool m_bDeleteOnClose;
+ int m_nCurrentChars;
+};
+
+
+CConsoleWnd::CConsoleWnd()
+{
+ m_hWnd = m_hEditControl = NULL;
+ m_bVisible = false;
+ m_bDeleteOnClose = false;
+ m_nCurrentChars = 0;
+}
+
+
+CConsoleWnd::~CConsoleWnd()
+{
+ Term();
+}
+
+bool CConsoleWnd::Init( void *hInstance, int dialogResourceID, int editControlID, bool bVisible )
+{
+ // Create the window.
+ m_hWnd = CreateDialog(
+ (HINSTANCE)hInstance,
+ MAKEINTRESOURCE( dialogResourceID ),
+ NULL,
+ &CConsoleWnd::StaticWindowProc );
+
+ if ( !m_hWnd )
+ return false;
+
+ SetWindowLong( m_hWnd, GWL_USERDATA, reinterpret_cast< LONG >( this ) );
+ if ( bVisible )
+ ShowWindow( m_hWnd, SW_SHOW );
+
+ // Get a handle to the edit control.
+ m_hEditControl = GetDlgItem( m_hWnd, editControlID );
+ if ( !m_hEditControl )
+ return false;
+
+ RepositionEditControl();
+
+ m_bVisible = bVisible;
+ return true;
+}
+
+
+void CConsoleWnd::Term()
+{
+ if ( m_hWnd )
+ {
+ DestroyWindow( m_hWnd );
+ m_hWnd = NULL;
+ }
+}
+
+
+void CConsoleWnd::Release()
+{
+ delete this;
+}
+
+
+void CConsoleWnd::SetVisible( bool bVisible )
+{
+ ShowWindow( m_hWnd, bVisible ? SW_RESTORE : SW_HIDE );
+
+ if ( bVisible )
+ {
+ ShowWindow( m_hWnd, SW_SHOW );
+ SetWindowPos( m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
+ UpdateWindow( m_hWnd );
+
+ int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 );
+ SendMessage( m_hEditControl, EM_SETSEL, nLen, nLen );
+ }
+ else
+ {
+ SetWindowPos( m_hWnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW | SWP_NOOWNERZORDER );
+ }
+
+ m_bVisible = bVisible;
+}
+
+
+bool CConsoleWnd::IsVisible() const
+{
+ return m_bVisible;
+}
+
+
+void CConsoleWnd::PrintToConsole( const char *pMsg )
+{
+ if ( m_nCurrentChars >= 16*1024 )
+ {
+ // Clear the edit control otherwise it'll stop outputting anything.
+ m_nCurrentChars = 0;
+
+ int nLen = (int)SendMessage( m_hEditControl, EM_GETLIMITTEXT, 0, 0 );
+ SendMessage( m_hEditControl, EM_SETSEL, 0, nLen );
+ SendMessage( m_hEditControl, EM_REPLACESEL, FALSE, (LPARAM)"" );
+ }
+
+ FormatAndSendToEditControl( m_hEditControl, pMsg );
+ m_nCurrentChars += (int)strlen( pMsg );
+}
+
+
+void CConsoleWnd::SetTitle( const char *pTitle )
+{
+ SetWindowText( m_hWnd, pTitle );
+}
+
+
+int CConsoleWnd::WindowProc(
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ lParam = lParam; // avoid compiler warning
+
+ if ( hwndDlg != m_hWnd )
+ return false;
+
+ switch ( uMsg )
+ {
+ case WM_SYSCOMMAND:
+ {
+ if ( wParam == SC_CLOSE )
+ {
+ if ( m_bDeleteOnClose )
+ {
+ Release();
+ }
+ else
+ {
+ SetVisible( false );
+ return true;
+ }
+ }
+ }
+ break;
+
+ case WM_SHOWWINDOW:
+ {
+ m_bVisible = (wParam != 0);
+ }
+ break;
+
+ case WM_SIZE:
+ case WM_INITDIALOG:
+ {
+ RepositionEditControl();
+ }
+ break;
+ }
+
+ return false;
+}
+
+
+int CConsoleWnd::StaticWindowProc(
+ HWND hwndDlg, // handle to dialog box
+ UINT uMsg, // message
+ WPARAM wParam, // first message parameter
+ LPARAM lParam // second message parameter
+ )
+{
+ CConsoleWnd *pDlg = (CConsoleWnd*)GetWindowLong( hwndDlg, GWL_USERDATA );
+ if ( pDlg )
+ return pDlg->WindowProc( hwndDlg, uMsg, wParam, lParam );
+ else
+ return false;
+}
+
+
+void CConsoleWnd::RepositionEditControl()
+{
+ RECT rcMain;
+ GetClientRect( m_hWnd, &rcMain );
+
+ RECT rcNew;
+ rcNew.left = rcMain.left + EDITCONTROL_BORDER_SIZE;
+ rcNew.right = rcMain.right - EDITCONTROL_BORDER_SIZE;
+ rcNew.top = rcMain.top + EDITCONTROL_BORDER_SIZE;
+ rcNew.bottom = rcMain.bottom - EDITCONTROL_BORDER_SIZE;
+
+ SetWindowPos(
+ m_hEditControl,
+ NULL,
+ rcNew.left,
+ rcNew.top,
+ rcNew.right - rcNew.left,
+ rcNew.bottom - rcNew.top,
+ SWP_NOZORDER );
+}
+
+
+void CConsoleWnd::SetDeleteOnClose( bool bDelete )
+{
+ m_bDeleteOnClose = bDelete;
+}
+
+
+// ------------------------------------------------------------------------------------ //
+// Module interface.
+// ------------------------------------------------------------------------------------ //
+
+void SendToEditControl( HWND hEditControl, const char *pText )
+{
+ int nLen = (int)SendMessage( hEditControl, EM_GETLIMITTEXT, 0, 0 );
+ SendMessage( hEditControl, EM_SETSEL, nLen, nLen );
+ SendMessage( hEditControl, EM_REPLACESEL, FALSE, (LPARAM)pText );
+}
+
+
+void FormatAndSendToEditControl( void *hWnd, const char *pText )
+{
+ HWND hEditControl = (HWND)hWnd;
+
+ // Translate \n to \r\n.
+ char outMsg[1024];
+ const char *pIn = pText;
+ char *pOut = outMsg;
+ while ( *pIn )
+ {
+ if ( *pIn == '\n' )
+ {
+ *pOut = '\r';
+ pOut++;
+ }
+ *pOut = *pIn;
+
+ ++pIn;
+ ++pOut;
+
+ if ( pOut - outMsg >= 1020 )
+ {
+ *pOut = 0;
+ SendToEditControl( hEditControl, outMsg );
+ pOut = outMsg;
+ }
+ }
+ *pOut = 0;
+ SendToEditControl( hEditControl, outMsg );
+}
+
+
+IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible )
+{
+ CConsoleWnd *pWnd = new CConsoleWnd;
+
+ if ( pWnd->Init( hInstance, dialogResourceID, editControlID, bVisible ) )
+ {
+ return pWnd;
+ }
+ else
+ {
+ pWnd->Release();
+ return NULL;
+ }
+}
+
+
+
+
diff --git a/mp/src/utils/common/consolewnd.h b/mp/src/utils/common/consolewnd.h new file mode 100644 index 00000000..4572ff57 --- /dev/null +++ b/mp/src/utils/common/consolewnd.h @@ -0,0 +1,45 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CONSOLEWND_H
+#define CONSOLEWND_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+class IConsoleWnd
+{
+public:
+ virtual void Release() = 0;
+
+ // Print a message to the console.
+ virtual void PrintToConsole( const char *pMsg ) = 0;
+
+ // Set the window title.
+ virtual void SetTitle( const char *pTitle ) = 0;
+
+ // Show and hide the console window.
+ virtual void SetVisible( bool bVisible ) = 0;
+ virtual bool IsVisible() const = 0;
+
+ // Normally, the window just hides itself when closed. You can use this to make the window
+ // automatically go away when they close it.
+ virtual void SetDeleteOnClose( bool bDelete ) = 0;
+};
+
+
+// Utility functions.
+
+// This converts adds \r's where necessary and sends the text to the edit control.
+void FormatAndSendToEditControl( void *hWnd, const char *pText );
+
+
+IConsoleWnd* CreateConsoleWnd( void *hInstance, int dialogResourceID, int editControlID, bool bVisible );
+
+
+#endif // CONSOLEWND_H
diff --git a/mp/src/utils/common/filesystem_tools.cpp b/mp/src/utils/common/filesystem_tools.cpp new file mode 100644 index 00000000..9714c57a --- /dev/null +++ b/mp/src/utils/common/filesystem_tools.cpp @@ -0,0 +1,209 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#if defined( _WIN32 ) && !defined( _X360 )
+#include <windows.h>
+#include <direct.h>
+#include <io.h> // _chmod
+#elif _LINUX
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include "tier1/strtools.h"
+#include "filesystem_tools.h"
+#include "tier0/icommandline.h"
+#include "KeyValues.h"
+#include "tier2/tier2.h"
+
+#ifdef MPI
+ #include "vmpi.h"
+ #include "vmpi_tools_shared.h"
+ #include "vmpi_filesystem.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// Module interface.
+// ---------------------------------------------------------------------------------------------------- //
+
+IBaseFileSystem *g_pFileSystem = NULL;
+
+// These are only used for tools that need the search paths that the engine's file system provides.
+CSysModule *g_pFullFileSystemModule = NULL;
+
+// ---------------------------------------------------------------------------
+//
+// These are the base paths that everything will be referenced relative to (textures especially)
+// All of these directories include the trailing slash
+//
+// ---------------------------------------------------------------------------
+
+// This is the the path of the initial source file (relative to the cwd)
+char qdir[1024];
+
+// This is the base engine + mod-specific game dir (e.g. "c:\tf2\mytfmod\")
+char gamedir[1024];
+
+void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath )
+{
+ // Set qdir.
+ if ( !pFilename )
+ {
+ pFilename = ".";
+ }
+
+ Q_MakeAbsolutePath( qdir, sizeof( qdir ), pFilename, NULL );
+ Q_StripFilename( qdir );
+ Q_strlower( qdir );
+ if ( qdir[0] != 0 )
+ {
+ Q_AppendSlash( qdir, sizeof( qdir ) );
+ }
+
+ // Set gamedir.
+ Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), pGameInfoPath );
+ Q_AppendSlash( gamedir, sizeof( gamedir ) );
+}
+
+
+bool FileSystem_Init_Normal( const char *pFilename, FSInitType_t initType, bool bOnlyUseDirectoryName )
+{
+ if ( initType == FS_INIT_FULL )
+ {
+ // First, get the name of the module
+ char fileSystemDLLName[MAX_PATH];
+ bool bSteam;
+ if ( FileSystem_GetFileSystemDLLName( fileSystemDLLName, MAX_PATH, bSteam ) != FS_OK )
+ return false;
+
+ // If we're under Steam we need extra setup to let us find the proper modules
+ FileSystem_SetupSteamInstallPath();
+
+ // Next, load the module, call Connect/Init.
+ CFSLoadModuleInfo loadModuleInfo;
+ loadModuleInfo.m_pFileSystemDLLName = fileSystemDLLName;
+ loadModuleInfo.m_pDirectoryName = pFilename;
+ loadModuleInfo.m_bOnlyUseDirectoryName = bOnlyUseDirectoryName;
+ loadModuleInfo.m_ConnectFactory = Sys_GetFactoryThis();
+ loadModuleInfo.m_bSteam = bSteam;
+ loadModuleInfo.m_bToolsMode = true;
+ if ( FileSystem_LoadFileSystemModule( loadModuleInfo ) != FS_OK )
+ return false;
+
+ // Next, mount the content
+ CFSMountContentInfo mountContentInfo;
+ mountContentInfo.m_pDirectoryName= loadModuleInfo.m_GameInfoPath;
+ mountContentInfo.m_pFileSystem = loadModuleInfo.m_pFileSystem;
+ mountContentInfo.m_bToolsMode = true;
+ if ( FileSystem_MountContent( mountContentInfo ) != FS_OK )
+ return false;
+
+ // Finally, load the search paths.
+ CFSSearchPathsInit searchPathsInit;
+ searchPathsInit.m_pDirectoryName = loadModuleInfo.m_GameInfoPath;
+ searchPathsInit.m_pFileSystem = loadModuleInfo.m_pFileSystem;
+ if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK )
+ return false;
+
+ // Store the data we got from filesystem_init.
+ g_pFileSystem = g_pFullFileSystem = loadModuleInfo.m_pFileSystem;
+ g_pFullFileSystemModule = loadModuleInfo.m_pModule;
+
+ FileSystem_AddSearchPath_Platform( g_pFullFileSystem, loadModuleInfo.m_GameInfoPath );
+
+ FileSystem_SetupStandardDirectories( pFilename, loadModuleInfo.m_GameInfoPath );
+ }
+ else
+ {
+ if ( !Sys_LoadInterface(
+ "filesystem_stdio",
+ FILESYSTEM_INTERFACE_VERSION,
+ &g_pFullFileSystemModule,
+ (void**)&g_pFullFileSystem ) )
+ {
+ return false;
+ }
+
+ if ( g_pFullFileSystem->Init() != INIT_OK )
+ return false;
+
+ g_pFullFileSystem->RemoveAllSearchPaths();
+ g_pFullFileSystem->AddSearchPath( "../platform", "PLATFORM" );
+ g_pFullFileSystem->AddSearchPath( ".", "GAME" );
+
+ g_pFileSystem = g_pFullFileSystem;
+ }
+
+ return true;
+}
+
+
+bool FileSystem_Init( const char *pBSPFilename, int maxMemoryUsage, FSInitType_t initType, bool bOnlyUseFilename )
+{
+ Assert( CommandLine()->GetCmdLine() != NULL ); // Should have called CreateCmdLine by now.
+
+ // If this app uses VMPI, then let VMPI intercept all filesystem calls.
+#if defined( MPI )
+ if ( g_bUseMPI )
+ {
+ if ( g_bMPIMaster )
+ {
+ if ( !FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename ) )
+ return false;
+
+ g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, g_pFullFileSystem );
+ SendQDirInfo();
+ }
+ else
+ {
+ g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Init( maxMemoryUsage, NULL );
+ RecvQDirInfo();
+ }
+ return true;
+ }
+#endif
+
+ return FileSystem_Init_Normal( pBSPFilename, initType, bOnlyUseFilename );
+}
+
+
+void FileSystem_Term()
+{
+#if defined( MPI )
+ if ( g_bUseMPI )
+ {
+ g_pFileSystem = g_pFullFileSystem = VMPI_FileSystem_Term();
+ }
+#endif
+
+ if ( g_pFullFileSystem )
+ {
+ g_pFullFileSystem->Shutdown();
+ g_pFullFileSystem = NULL;
+ g_pFileSystem = NULL;
+ }
+
+ if ( g_pFullFileSystemModule )
+ {
+ Sys_UnloadModule( g_pFullFileSystemModule );
+ g_pFullFileSystemModule = NULL;
+ }
+}
+
+
+CreateInterfaceFn FileSystem_GetFactory()
+{
+#if defined( MPI )
+ if ( g_bUseMPI )
+ return VMPI_FileSystem_GetFactory();
+#endif
+ return Sys_GetFactory( g_pFullFileSystemModule );
+}
diff --git a/mp/src/utils/common/filesystem_tools.h b/mp/src/utils/common/filesystem_tools.h new file mode 100644 index 00000000..09db7b3e --- /dev/null +++ b/mp/src/utils/common/filesystem_tools.h @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#ifndef FILESYSTEM_TOOLS_H
+#define FILESYSTEM_TOOLS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "filesystem.h"
+#include "filesystem_init.h"
+
+
+// This is the the path of the initial source file
+extern char qdir[1024];
+
+// This is the base engine + mod-specific game dir (e.g. "d:\tf2\mytfmod\")
+extern char gamedir[1024];
+
+
+// ---------------------------------------------------------------------------------------- //
+// Filesystem initialization.
+// ---------------------------------------------------------------------------------------- //
+
+enum FSInitType_t
+{
+ FS_INIT_FULL, // Load gameinfo.txt, maybe use filesystem_steam, and setup search paths.
+ FS_INIT_COMPATIBILITY_MODE // Load filesystem_stdio and that's it.
+};
+
+//
+// Initializes qdir, and gamedir. Also initializes the VMPI filesystem if MPI is defined.
+//
+// pFilename can be NULL if you want to rely on vproject and qproject. If it's specified, FileSystem_Init
+// will go up directories from pFilename looking for gameinfo.txt (if vproject isn't specified).
+//
+// If bOnlyUseFilename is true, then it won't use any alternative methods of finding the vproject dir
+// (ie: it won't use -game or -vproject or the vproject env var or qproject).
+//
+bool FileSystem_Init( const char *pFilename, int maxMemoryUsage=0, FSInitType_t initType=FS_INIT_FULL, bool bOnlyUseFilename=false );
+void FileSystem_Term();
+
+// Used to connect app-framework based console apps to the filesystem tools
+void FileSystem_SetupStandardDirectories( const char *pFilename, const char *pGameInfoPath );
+
+CreateInterfaceFn FileSystem_GetFactory( void );
+
+
+extern IBaseFileSystem *g_pFileSystem;
+extern IFileSystem *g_pFullFileSystem; // NOTE: this is here when VMPI is being used, but a VMPI app can
+ // ONLY use LoadModule/UnloadModule.
+
+
+#endif // FILESYSTEM_TOOLS_H
diff --git a/mp/src/utils/common/map_shared.cpp b/mp/src/utils/common/map_shared.cpp new file mode 100644 index 00000000..f1e970c0 --- /dev/null +++ b/mp/src/utils/common/map_shared.cpp @@ -0,0 +1,136 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "map_shared.h"
+#include "bsplib.h"
+#include "cmdlib.h"
+
+
+CMapError g_MapError;
+int g_nMapFileVersion;
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pLoadEntity -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
+{
+ if (!stricmp(szKey, "classname"))
+ {
+ if (!stricmp(szValue, "func_detail"))
+ {
+ pLoadEntity->nBaseContents = CONTENTS_DETAIL;
+ }
+ else if (!stricmp(szValue, "func_ladder"))
+ {
+ pLoadEntity->nBaseContents = CONTENTS_LADDER;
+ }
+ else if (!stricmp(szValue, "func_water"))
+ {
+ pLoadEntity->nBaseContents = CONTENTS_WATER;
+ }
+ }
+ else if (!stricmp(szKey, "id"))
+ {
+ // UNDONE: flag entity errors by ID instead of index
+ //g_MapError.EntityState( atoi( szValue ) );
+ // rename this field since DME code uses this name
+ SetKeyValue( pLoadEntity->pEntity, "hammerid", szValue );
+ return(ChunkFile_Ok);
+ }
+ else if( !stricmp( szKey, "mapversion" ) )
+ {
+ // .vmf map revision number
+ g_MapRevision = atoi( szValue );
+ SetKeyValue( pLoadEntity->pEntity, szKey, szValue );
+ return ( ChunkFile_Ok );
+ }
+
+ SetKeyValue( pLoadEntity->pEntity, szKey, szValue );
+
+ return(ChunkFile_Ok);
+}
+
+
+static ChunkFileResult_t LoadEntityCallback( CChunkFile *pFile, int nParam )
+{
+ if (num_entities == MAX_MAP_ENTITIES)
+ {
+ // Exits.
+ g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES");
+ }
+
+ entity_t *mapent = &entities[num_entities];
+ num_entities++;
+ memset(mapent, 0, sizeof(*mapent));
+ mapent->numbrushes = 0;
+
+ LoadEntity_t LoadEntity;
+ LoadEntity.pEntity = mapent;
+
+ // No default flags/contents
+ LoadEntity.nBaseFlags = 0;
+ LoadEntity.nBaseContents = 0;
+
+ //
+ // Read the entity chunk.
+ //
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity);
+
+ return eResult;
+}
+
+
+bool LoadEntsFromMapFile( char const *pFilename )
+{
+ //
+ // Dummy this up for the texture handling. This can be removed when old .MAP file
+ // support is removed.
+ //
+ g_nMapFileVersion = 400;
+
+ //
+ // Open the file.
+ //
+ CChunkFile File;
+ ChunkFileResult_t eResult = File.Open( pFilename, ChunkFile_Read );
+
+ if(eResult == ChunkFile_Ok)
+ {
+ num_entities = 0;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0);
+
+ File.PushHandlers(&Handlers);
+
+ //
+ // Read the sub-chunks. We ignore keys in the root of the file.
+ //
+ while (eResult == ChunkFile_Ok)
+ {
+ eResult = File.ReadChunk();
+ }
+
+ File.PopHandlers();
+ return true;
+ }
+ else
+ {
+ Error("Error in LoadEntsFromMapFile (in-memory file): %s.\n", File.GetErrorText(eResult));
+ return false;
+ }
+}
+
+
diff --git a/mp/src/utils/common/map_shared.h b/mp/src/utils/common/map_shared.h new file mode 100644 index 00000000..08e443a0 --- /dev/null +++ b/mp/src/utils/common/map_shared.h @@ -0,0 +1,91 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MAP_SHARED_H
+#define MAP_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "ChunkFile.h"
+#include "bsplib.h"
+#include "cmdlib.h"
+
+
+struct LoadEntity_t
+{
+ entity_t *pEntity;
+ int nID;
+ int nBaseFlags;
+ int nBaseContents;
+};
+
+
+class CMapError
+{
+public:
+
+ void BrushState( int brushID )
+ {
+ m_brushID = brushID;
+ }
+
+ void BrushSide( int side )
+ {
+ m_sideIndex = side;
+ }
+
+ void TextureState( const char *pTextureName )
+ {
+ Q_strncpy( m_textureName, pTextureName, sizeof( m_textureName ) );
+ }
+
+ void ClearState( void )
+ {
+ BrushState( 0 );
+ BrushSide( 0 );
+ TextureState( "Not a Parse error!" );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Hook the map parse errors and report brush/ent/texture state
+ // Input : *pErrorString -
+ //-----------------------------------------------------------------------------
+ void ReportError( const char *pErrorString )
+ {
+ Error( "Brush %i: %s\nSide %i\nTexture: %s\n", m_brushID, pErrorString, m_sideIndex, m_textureName );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Hook the map parse errors and report brush/ent/texture state without exiting.
+ // Input : pWarningString -
+ //-----------------------------------------------------------------------------
+ void ReportWarning( const char *pWarningString )
+ {
+ printf( "Brush %i, Side %i: %s\n", m_brushID, m_sideIndex, pWarningString );
+ }
+
+private:
+
+ int m_brushID;
+ int m_sideIndex;
+ char m_textureName[80];
+};
+
+
+extern CMapError g_MapError;
+extern int g_nMapFileVersion;
+
+
+// Shared mapload code.
+ChunkFileResult_t LoadEntityKeyCallback( const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity );
+
+// Used by VRAD incremental lighting - only load ents from the file and
+// fill in the global entities/num_entities array.
+bool LoadEntsFromMapFile( char const *pFilename );
+
+#endif // MAP_SHARED_H
diff --git a/mp/src/utils/common/movie.h b/mp/src/utils/common/movie.h new file mode 100644 index 00000000..78ba92fb --- /dev/null +++ b/mp/src/utils/common/movie.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#ifndef _MOVIE_H_
+#define _MOVIE_H_
+
+/*
+ movie.h
+
+ definitions and such for dumping screen shots to make a movie
+*/
+
+typedef struct
+{
+ unsigned long tag;
+ unsigned long size;
+} movieblockheader_t;
+
+
+typedef struct
+{
+ short width;
+ short height;
+ short depth;
+} movieframe_t;
+
+
+
+#endif _MOVIE_H_
\ No newline at end of file diff --git a/mp/src/utils/common/mpi_stats.cpp b/mp/src/utils/common/mpi_stats.cpp new file mode 100644 index 00000000..8d9cc5e7 --- /dev/null +++ b/mp/src/utils/common/mpi_stats.cpp @@ -0,0 +1,839 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Nasty headers!
+#include "MySqlDatabase.h"
+#include "tier1/strtools.h"
+#include "vmpi.h"
+#include "vmpi_dispatch.h"
+#include "mpi_stats.h"
+#include "cmdlib.h"
+#include "imysqlwrapper.h"
+#include "threadhelpers.h"
+#include "vmpi_tools_shared.h"
+#include "tier0/icommandline.h"
+
+/*
+
+-- MySQL code to create the databases, create the users, and set access privileges.
+-- You only need to ever run this once.
+
+create database vrad;
+
+use mysql;
+
+create user vrad_worker;
+create user vmpi_browser;
+
+-- This updates the "user" table, which is checked when someone tries to connect to the database.
+grant select,insert,update on vrad.* to vrad_worker;
+grant select on vrad.* to vmpi_browser;
+flush privileges;
+
+/*
+
+-- SQL code to (re)create the tables.
+
+-- Master generates a unique job ID (in job_master_start) and sends it to workers.
+-- Each worker (and the master) make a job_worker_start, link it to the primary job ID,
+-- get their own unique ID, which represents that process in that job.
+-- All JobWorkerID fields link to the JobWorkerID field in job_worker_start.
+
+-- NOTE: do a "use vrad" or "use vvis" first, depending on the DB you want to create.
+
+
+use vrad;
+
+
+drop table job_master_start;
+create table job_master_start (
+ JobID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, index id( JobID, MachineName(5) ),
+ BSPFilename TINYTEXT NOT NULL,
+ StartTime TIMESTAMP NOT NULL,
+ MachineName TEXT NOT NULL,
+ RunningTimeMS INTEGER UNSIGNED NOT NULL,
+ NumWorkers INTEGER UNSIGNED NOT NULL default 0
+ );
+
+drop table job_master_end;
+create table job_master_end (
+ JobID INTEGER UNSIGNED NOT NULL, PRIMARY KEY ( JobID ),
+ NumWorkersConnected SMALLINT UNSIGNED NOT NULL,
+ NumWorkersDisconnected SMALLINT UNSIGNED NOT NULL,
+ ErrorText TEXT NOT NULL
+ );
+
+drop table job_worker_start;
+create table job_worker_start (
+ JobWorkerID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
+ index index_jobid( JobID ),
+ index index_jobworkerid( JobWorkerID ),
+
+ JobID INTEGER UNSIGNED NOT NULL, -- links to job_master_start::JobID
+ IsMaster BOOL NOT NULL, -- Set to 1 if this "worker" is the master process.
+ RunningTimeMS INTEGER UNSIGNED NOT NULL default 0,
+ MachineName TEXT NOT NULL,
+ WorkerState SMALLINT UNSIGNED NOT NULL default 0, -- 0 = disconnected, 1 = connected
+ NumWorkUnits INTEGER UNSIGNED NOT NULL default 0, -- how many work units this worker has completed
+ CurrentStage TINYTEXT NOT NULL, -- which compile stage is it on
+ Thread0WU INTEGER NOT NULL default 0, -- which WU thread 0 is on
+ Thread1WU INTEGER NOT NULL default 0, -- which WU thread 1 is on
+ Thread2WU INTEGER NOT NULL default 0, -- which WU thread 2 is on
+ Thread3WU INTEGER NOT NULL default 0 -- which WU thread 3 is on
+ );
+
+drop table text_messages;
+create table text_messages (
+ JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID, MessageIndex ),
+ MessageIndex INTEGER UNSIGNED NOT NULL,
+ Text TEXT NOT NULL
+ );
+
+drop table graph_entry;
+create table graph_entry (
+ JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ),
+ MSSinceJobStart INTEGER UNSIGNED NOT NULL,
+ BytesSent INTEGER UNSIGNED NOT NULL,
+ BytesReceived INTEGER UNSIGNED NOT NULL
+ );
+
+drop table events;
+create table events (
+ JobWorkerID INTEGER UNSIGNED NOT NULL, index id( JobWorkerID ),
+ Text TEXT NOT NULL
+ );
+*/
+
+
+
+// Stats set by the app.
+int g_nWorkersConnected = 0;
+int g_nWorkersDisconnected = 0;
+
+
+DWORD g_StatsStartTime;
+
+CMySqlDatabase *g_pDB = NULL;
+
+IMySQL *g_pSQL = NULL;
+CSysModule *g_hMySQLDLL = NULL;
+
+char g_BSPFilename[256];
+
+bool g_bMaster = false;
+unsigned long g_JobPrimaryID = 0; // This represents this job, but doesn't link to a particular machine.
+unsigned long g_JobWorkerID = 0; // A unique key in the DB that represents this machine in this job.
+char g_MachineName[MAX_COMPUTERNAME_LENGTH+1] = {0};
+
+unsigned long g_CurrentMessageIndex = 0;
+
+
+HANDLE g_hPerfThread = NULL;
+DWORD g_PerfThreadID = 0xFEFEFEFE;
+HANDLE g_hPerfThreadExitEvent = NULL;
+
+// These are set by the app and they go into the database.
+extern uint64 g_ThreadWUs[4];
+
+extern uint64 VMPI_GetNumWorkUnitsCompleted( int iProc );
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// This is a helper class to build queries like the stream IO.
+// ---------------------------------------------------------------------------------------------------- //
+
+class CMySQLQuery
+{
+friend class CMySQL;
+
+public:
+ // This is like a sprintf, but it will grow the string as necessary.
+ void Format( const char *pFormat, ... );
+
+ int Execute( IMySQL *pDB );
+
+private:
+ CUtlVector<char> m_QueryText;
+};
+
+
+void CMySQLQuery::Format( const char *pFormat, ... )
+{
+ #define QUERYTEXT_GROWSIZE 1024
+
+ // This keeps growing the buffer and calling _vsnprintf until the buffer is
+ // large enough to hold all the data.
+ m_QueryText.SetSize( QUERYTEXT_GROWSIZE );
+ while ( 1 )
+ {
+ va_list marker;
+ va_start( marker, pFormat );
+ int ret = _vsnprintf( m_QueryText.Base(), m_QueryText.Count(), pFormat, marker );
+ va_end( marker );
+
+ if ( ret < 0 )
+ {
+ m_QueryText.SetSize( m_QueryText.Count() + QUERYTEXT_GROWSIZE );
+ }
+ else
+ {
+ m_QueryText[ m_QueryText.Count() - 1 ] = 0;
+ break;
+ }
+ }
+}
+
+
+int CMySQLQuery::Execute( IMySQL *pDB )
+{
+ int ret = pDB->Execute( m_QueryText.Base() );
+ m_QueryText.Purge();
+ return ret;
+}
+
+
+
+// ---------------------------------------------------------------------------------------------------- //
+// This inserts the necessary backslashes in front of backslashes or quote characters.
+// ---------------------------------------------------------------------------------------------------- //
+
+char* FormatStringForSQL( const char *pText )
+{
+ // First, count the quotes in the string. We need to put a backslash in front of each one.
+ int nChars = 0;
+ const char *pCur = pText;
+ while ( *pCur != 0 )
+ {
+ if ( *pCur == '\"' || *pCur == '\\' )
+ ++nChars;
+
+ ++pCur;
+ ++nChars;
+ }
+
+ pCur = pText;
+ char *pRetVal = new char[nChars+1];
+ for ( int i=0; i < nChars; )
+ {
+ if ( *pCur == '\"' || *pCur == '\\' )
+ pRetVal[i++] = '\\';
+
+ pRetVal[i++] = *pCur;
+ ++pCur;
+ }
+ pRetVal[nChars] = 0;
+
+ return pRetVal;
+}
+
+
+
+// -------------------------------------------------------------------------------- //
+// Commands to add data to the database.
+// -------------------------------------------------------------------------------- //
+class CSQLDBCommandBase : public ISQLDBCommand
+{
+public:
+ virtual ~CSQLDBCommandBase()
+ {
+ }
+
+ virtual void deleteThis()
+ {
+ delete this;
+ }
+};
+
+class CSQLDBCommand_WorkerStats : public CSQLDBCommandBase
+{
+public:
+ virtual int RunCommand()
+ {
+ int nCurConnections = VMPI_GetCurrentNumberOfConnections();
+
+
+ // Update the NumWorkers entry.
+ char query[2048];
+ Q_snprintf( query, sizeof( query ), "update job_master_start set NumWorkers=%d where JobID=%lu",
+ nCurConnections,
+ g_JobPrimaryID );
+ g_pSQL->Execute( query );
+
+
+ // Update the job_master_worker_stats stuff.
+ for ( int i=1; i < nCurConnections; i++ )
+ {
+ unsigned long jobWorkerID = VMPI_GetJobWorkerID( i );
+
+ if ( jobWorkerID != 0xFFFFFFFF )
+ {
+ Q_snprintf( query, sizeof( query ), "update "
+ "job_worker_start set WorkerState=%d, NumWorkUnits=%d where JobWorkerID=%lu",
+ VMPI_IsProcConnected( i ),
+ (int) VMPI_GetNumWorkUnitsCompleted( i ),
+ VMPI_GetJobWorkerID( i )
+ );
+ g_pSQL->Execute( query );
+ }
+ }
+ return 1;
+ }
+};
+
+class CSQLDBCommand_JobMasterEnd : public CSQLDBCommandBase
+{
+public:
+
+ virtual int RunCommand()
+ {
+ CMySQLQuery query;
+ query.Format( "insert into job_master_end values ( %lu, %d, %d, \"no errors\" )", g_JobPrimaryID, g_nWorkersConnected, g_nWorkersDisconnected );
+ query.Execute( g_pSQL );
+
+ // Now set RunningTimeMS.
+ unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime;
+ query.Format( "update job_master_start set RunningTimeMS=%lu where JobID=%lu", runningTimeMS, g_JobPrimaryID );
+ query.Execute( g_pSQL );
+ return 1;
+ }
+};
+
+
+void UpdateJobWorkerRunningTime()
+{
+ unsigned long runningTimeMS = GetTickCount() - g_StatsStartTime;
+
+ char curStage[256];
+ VMPI_GetCurrentStage( curStage, sizeof( curStage ) );
+
+ CMySQLQuery query;
+ query.Format( "update job_worker_start set RunningTimeMS=%lu, CurrentStage=\"%s\", "
+ "Thread0WU=%d, Thread1WU=%d, Thread2WU=%d, Thread3WU=%d where JobWorkerID=%lu",
+ runningTimeMS,
+ curStage,
+ (int) g_ThreadWUs[0],
+ (int) g_ThreadWUs[1],
+ (int) g_ThreadWUs[2],
+ (int) g_ThreadWUs[3],
+ g_JobWorkerID );
+ query.Execute( g_pSQL );
+}
+
+
+class CSQLDBCommand_GraphEntry : public CSQLDBCommandBase
+{
+public:
+
+ CSQLDBCommand_GraphEntry( DWORD msTime, DWORD nBytesSent, DWORD nBytesReceived )
+ {
+ m_msTime = msTime;
+ m_nBytesSent = nBytesSent;
+ m_nBytesReceived = nBytesReceived;
+ }
+
+ virtual int RunCommand()
+ {
+ CMySQLQuery query;
+ query.Format( "insert into graph_entry (JobWorkerID, MSSinceJobStart, BytesSent, BytesReceived) "
+ "values ( %lu, %lu, %lu, %lu )",
+ g_JobWorkerID,
+ m_msTime,
+ m_nBytesSent,
+ m_nBytesReceived );
+
+ query.Execute( g_pSQL );
+
+ UpdateJobWorkerRunningTime();
+
+ ++g_CurrentMessageIndex;
+ return 1;
+ }
+
+ DWORD m_nBytesSent;
+ DWORD m_nBytesReceived;
+ DWORD m_msTime;
+};
+
+
+
+class CSQLDBCommand_TextMessage : public CSQLDBCommandBase
+{
+public:
+
+ CSQLDBCommand_TextMessage( const char *pText )
+ {
+ m_pText = FormatStringForSQL( pText );
+ }
+
+ virtual ~CSQLDBCommand_TextMessage()
+ {
+ delete [] m_pText;
+ }
+
+ virtual int RunCommand()
+ {
+ CMySQLQuery query;
+ query.Format( "insert into text_messages (JobWorkerID, MessageIndex, Text) values ( %lu, %lu, \"%s\" )", g_JobWorkerID, g_CurrentMessageIndex, m_pText );
+ query.Execute( g_pSQL );
+
+ ++g_CurrentMessageIndex;
+ return 1;
+ }
+
+ char *m_pText;
+};
+
+
+// -------------------------------------------------------------------------------- //
+// Internal helpers.
+// -------------------------------------------------------------------------------- //
+
+// This is the spew output before it has connected to the MySQL database.
+CCriticalSection g_SpewTextCS;
+CUtlVector<char> g_SpewText( 1024 );
+
+
+void VMPI_Stats_SpewHook( const char *pMsg )
+{
+ CCriticalSectionLock csLock( &g_SpewTextCS );
+ csLock.Lock();
+
+ // Queue the text up so we can send it to the DB right away when we connect.
+ g_SpewText.AddMultipleToTail( strlen( pMsg ), pMsg );
+}
+
+
+void PerfThread_SendSpewText()
+{
+ // Send the spew text to the database.
+ CCriticalSectionLock csLock( &g_SpewTextCS );
+ csLock.Lock();
+
+ if ( g_SpewText.Count() > 0 )
+ {
+ g_SpewText.AddToTail( 0 );
+
+ if ( g_bMPI_StatsTextOutput )
+ {
+ g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( g_SpewText.Base() ), NULL );
+ }
+ else
+ {
+ // Just show one message in the vmpi_job_watch window to let them know that they need
+ // to use a command line option to get the output.
+ static bool bFirst = true;
+ if ( bFirst )
+ {
+ char msg[512];
+ V_snprintf( msg, sizeof( msg ), "%s not enabled", VMPI_GetParamString( mpi_Stats_TextOutput ) );
+ bFirst = false;
+ g_pDB->AddCommandToQueue( new CSQLDBCommand_TextMessage( msg ), NULL );
+ }
+ }
+
+ g_SpewText.RemoveAll();
+ }
+
+ csLock.Unlock();
+}
+
+
+void PerfThread_AddGraphEntry( DWORD startTicks, DWORD &lastSent, DWORD &lastReceived )
+{
+ // Send the graph entry with data transmission info.
+ DWORD curSent = g_nBytesSent + g_nMulticastBytesSent;
+ DWORD curReceived = g_nBytesReceived + g_nMulticastBytesReceived;
+
+ g_pDB->AddCommandToQueue(
+ new CSQLDBCommand_GraphEntry(
+ GetTickCount() - startTicks,
+ curSent - lastSent,
+ curReceived - lastReceived ),
+ NULL );
+
+ lastSent = curSent;
+ lastReceived = curReceived;
+}
+
+
+// This function adds a graph_entry into the database periodically.
+DWORD WINAPI PerfThreadFn( LPVOID pParameter )
+{
+ DWORD lastSent = 0;
+ DWORD lastReceived = 0;
+ DWORD startTicks = GetTickCount();
+
+ while ( WaitForSingleObject( g_hPerfThreadExitEvent, 1000 ) != WAIT_OBJECT_0 )
+ {
+ PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived );
+
+ // Send updates for text output.
+ PerfThread_SendSpewText();
+
+ // If we're the master, update all the worker stats.
+ if ( g_bMaster )
+ {
+ g_pDB->AddCommandToQueue(
+ new CSQLDBCommand_WorkerStats,
+ NULL );
+ }
+ }
+
+ // Add the remaining text and one last graph entry (which will include the current stage info).
+ PerfThread_SendSpewText();
+ PerfThread_AddGraphEntry( startTicks, lastSent, lastReceived );
+
+ SetEvent( g_hPerfThreadExitEvent );
+ return 0;
+}
+
+
+// -------------------------------------------------------------------------------- //
+// VMPI_Stats interface.
+// -------------------------------------------------------------------------------- //
+
+void VMPI_Stats_InstallSpewHook()
+{
+ InstallExtraSpewHook( VMPI_Stats_SpewHook );
+}
+
+
+void UnloadMySQLWrapper()
+{
+ if ( g_hMySQLDLL )
+ {
+ if ( g_pSQL )
+ {
+ g_pSQL->Release();
+ g_pSQL = NULL;
+ }
+
+ Sys_UnloadModule( g_hMySQLDLL );
+ g_hMySQLDLL = NULL;
+ }
+}
+
+
+bool LoadMySQLWrapper(
+ const char *pHostName,
+ const char *pDBName,
+ const char *pUserName
+ )
+{
+ UnloadMySQLWrapper();
+
+ // Load the DLL and the interface.
+ if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &g_hMySQLDLL, (void**)&g_pSQL ) )
+ return false;
+
+ // Try to init the database.
+ if ( !g_pSQL->InitMySQL( pDBName, pHostName, pUserName ) )
+ {
+ UnloadMySQLWrapper();
+ return false;
+ }
+
+ return true;
+}
+
+
+bool VMPI_Stats_Init_Master(
+ const char *pHostName,
+ const char *pDBName,
+ const char *pUserName,
+ const char *pBSPFilename,
+ unsigned long *pDBJobID )
+{
+ Assert( !g_pDB );
+
+ g_bMaster = true;
+
+ // Connect the database.
+ g_pDB = new CMySqlDatabase;
+ if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) )
+ {
+ delete g_pDB;
+ g_pDB = NULL;
+ return false;
+ }
+
+ DWORD size = sizeof( g_MachineName );
+ GetComputerName( g_MachineName, &size );
+
+ // Create the job_master_start row.
+ Q_FileBase( pBSPFilename, g_BSPFilename, sizeof( g_BSPFilename ) );
+
+ g_JobPrimaryID = 0;
+ CMySQLQuery query;
+ query.Format( "insert into job_master_start ( BSPFilename, StartTime, MachineName, RunningTimeMS ) values ( \"%s\", null, \"%s\", %lu )", g_BSPFilename, g_MachineName, RUNNINGTIME_MS_SENTINEL );
+ query.Execute( g_pSQL );
+
+ g_JobPrimaryID = g_pSQL->InsertID();
+ if ( g_JobPrimaryID == 0 )
+ {
+ delete g_pDB;
+ g_pDB = NULL;
+ return false;
+ }
+
+
+ // Now init the worker portion.
+ *pDBJobID = g_JobPrimaryID;
+ return VMPI_Stats_Init_Worker( NULL, NULL, NULL, g_JobPrimaryID );
+}
+
+
+
+bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID )
+{
+ g_StatsStartTime = GetTickCount();
+
+ // If pDBServerName is null, then we're the master and we just want to make the job_worker_start entry.
+ if ( pHostName )
+ {
+ Assert( !g_pDB );
+
+ // Connect the database.
+ g_pDB = new CMySqlDatabase;
+ if ( !g_pDB || !g_pDB->Initialize() || !LoadMySQLWrapper( pHostName, pDBName, pUserName ) )
+ {
+ delete g_pDB;
+ g_pDB = NULL;
+ return false;
+ }
+
+ // Get our machine name to store in the database.
+ DWORD size = sizeof( g_MachineName );
+ GetComputerName( g_MachineName, &size );
+ }
+
+
+ g_JobPrimaryID = DBJobID;
+ g_JobWorkerID = 0;
+
+ CMySQLQuery query;
+ query.Format( "insert into job_worker_start ( JobID, CurrentStage, IsMaster, MachineName ) values ( %lu, \"none\", %d, \"%s\" )",
+ g_JobPrimaryID, g_bMaster, g_MachineName );
+ query.Execute( g_pSQL );
+
+ g_JobWorkerID = g_pSQL->InsertID();
+ if ( g_JobWorkerID == 0 )
+ {
+ delete g_pDB;
+ g_pDB = NULL;
+ return false;
+ }
+
+ // Now create a thread that samples perf data and stores it in the database.
+ g_hPerfThreadExitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ g_hPerfThread = CreateThread(
+ NULL,
+ 0,
+ PerfThreadFn,
+ NULL,
+ 0,
+ &g_PerfThreadID );
+
+ return true;
+}
+
+
+void VMPI_Stats_Term()
+{
+ if ( !g_pDB )
+ return;
+
+ // Stop the thread.
+ SetEvent( g_hPerfThreadExitEvent );
+ WaitForSingleObject( g_hPerfThread, INFINITE );
+
+ CloseHandle( g_hPerfThreadExitEvent );
+ g_hPerfThreadExitEvent = NULL;
+
+ CloseHandle( g_hPerfThread );
+ g_hPerfThread = NULL;
+
+ if ( g_bMaster )
+ {
+ // (Write a job_master_end entry here).
+ g_pDB->AddCommandToQueue( new CSQLDBCommand_JobMasterEnd, NULL );
+ }
+
+ // Wait for up to a second for the DB to finish writing its data.
+ DWORD startTime = GetTickCount();
+ while ( GetTickCount() - startTime < 1000 )
+ {
+ if ( g_pDB->QueriesInOutQueue() == 0 )
+ break;
+ }
+
+ delete g_pDB;
+ g_pDB = NULL;
+
+ UnloadMySQLWrapper();
+}
+
+
+static bool ReadStringFromFile( FILE *fp, char *pStr, int strSize )
+{
+ int i=0;
+ for ( i; i < strSize-2; i++ )
+ {
+ if ( fread( &pStr[i], 1, 1, fp ) != 1 ||
+ pStr[i] == '\n' )
+ {
+ break;
+ }
+ }
+
+ pStr[i] = 0;
+ return i != 0;
+}
+
+
+// This looks for pDBInfoFilename in the same path as pBaseExeFilename.
+// The file has 3 lines: machine name (with database), database name, username
+void GetDBInfo( const char *pDBInfoFilename, CDBInfo *pInfo )
+{
+ char baseExeFilename[512];
+ if ( !GetModuleFileName( GetModuleHandle( NULL ), baseExeFilename, sizeof( baseExeFilename ) ) )
+ Error( "GetModuleFileName failed." );
+
+ // Look for the info file in the same directory as the exe.
+ char dbInfoFilename[512];
+ Q_strncpy( dbInfoFilename, baseExeFilename, sizeof( dbInfoFilename ) );
+ Q_StripFilename( dbInfoFilename );
+
+ if ( dbInfoFilename[0] == 0 )
+ Q_strncpy( dbInfoFilename, ".", sizeof( dbInfoFilename ) );
+
+ Q_strncat( dbInfoFilename, "/", sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS );
+ Q_strncat( dbInfoFilename, pDBInfoFilename, sizeof( dbInfoFilename ), COPY_ALL_CHARACTERS );
+
+ FILE *fp = fopen( dbInfoFilename, "rt" );
+ if ( !fp )
+ {
+ Error( "Can't open %s for database info.\n", dbInfoFilename );
+ }
+
+ if ( !ReadStringFromFile( fp, pInfo->m_HostName, sizeof( pInfo->m_HostName ) ) ||
+ !ReadStringFromFile( fp, pInfo->m_DBName, sizeof( pInfo->m_DBName ) ) ||
+ !ReadStringFromFile( fp, pInfo->m_UserName, sizeof( pInfo->m_UserName ) )
+ )
+ {
+ Error( "%s is not a valid database info file.\n", dbInfoFilename );
+ }
+
+ fclose( fp );
+}
+
+
+void RunJobWatchApp( char *pCmdLine )
+{
+ STARTUPINFO si;
+ memset( &si, 0, sizeof( si ) );
+ si.cb = sizeof( si );
+
+ PROCESS_INFORMATION pi;
+ memset( &pi, 0, sizeof( pi ) );
+
+ // Working directory should be the same as our exe's directory.
+ char dirName[512];
+ if ( GetModuleFileName( NULL, dirName, sizeof( dirName ) ) != 0 )
+ {
+ char *s1 = V_strrchr( dirName, '\\' );
+ char *s2 = V_strrchr( dirName, '/' );
+ if ( s1 || s2 )
+ {
+ // Get rid of the last slash.
+ s1 = max( s1, s2 );
+ s1[0] = 0;
+
+ if ( !CreateProcess(
+ NULL,
+ pCmdLine,
+ NULL, // security
+ NULL,
+ TRUE,
+ 0, // flags
+ NULL, // environment
+ dirName, // current directory
+ &si,
+ &pi ) )
+ {
+ Warning( "%s - error launching '%s'\n", VMPI_GetParamString( mpi_Job_Watch ), pCmdLine );
+ }
+ }
+ }
+}
+
+
+void StatsDB_InitStatsDatabase(
+ int argc,
+ char **argv,
+ const char *pDBInfoFilename )
+{
+ // Did they disable the stats database?
+ if ( !g_bMPI_Stats && !VMPI_IsParamUsed( mpi_Job_Watch ) )
+ return;
+
+ unsigned long jobPrimaryID;
+
+ // Now open the DB.
+ if ( g_bMPIMaster )
+ {
+ CDBInfo dbInfo;
+ GetDBInfo( pDBInfoFilename, &dbInfo );
+
+ if ( !VMPI_Stats_Init_Master( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, argv[argc-1], &jobPrimaryID ) )
+ {
+ Warning( "VMPI_Stats_Init_Master( %s, %s, %s ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName );
+
+ // Tell the workers not to use stats.
+ dbInfo.m_HostName[0] = 0;
+ }
+
+ char cmdLine[2048];
+ Q_snprintf( cmdLine, sizeof( cmdLine ), "vmpi_job_watch -JobID %d", jobPrimaryID );
+
+ Msg( "\nTo watch this job, run this command line:\n%s\n\n", cmdLine );
+
+ if ( VMPI_IsParamUsed( mpi_Job_Watch ) )
+ {
+ // Convenience thing to automatically launch the job watch for this job.
+ RunJobWatchApp( cmdLine );
+ }
+
+ // Send the database info to all the workers.
+ SendDBInfo( &dbInfo, jobPrimaryID );
+ }
+ else
+ {
+ // Wait to get DB info so we can connect to the MySQL database.
+ CDBInfo dbInfo;
+ unsigned long jobPrimaryID;
+ RecvDBInfo( &dbInfo, &jobPrimaryID );
+
+ if ( dbInfo.m_HostName[0] != 0 )
+ {
+ if ( !VMPI_Stats_Init_Worker( dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID ) )
+ Error( "VMPI_Stats_Init_Worker( %s, %s, %s, %d ) failed.\n", dbInfo.m_HostName, dbInfo.m_DBName, dbInfo.m_UserName, jobPrimaryID );
+ }
+ }
+}
+
+
+unsigned long StatsDB_GetUniqueJobID()
+{
+ return g_JobPrimaryID;
+}
+
+
+unsigned long VMPI_Stats_GetJobWorkerID()
+{
+ return g_JobWorkerID;
+}
\ No newline at end of file diff --git a/mp/src/utils/common/mpi_stats.h b/mp/src/utils/common/mpi_stats.h new file mode 100644 index 00000000..10aa6162 --- /dev/null +++ b/mp/src/utils/common/mpi_stats.h @@ -0,0 +1,59 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MPI_STATS_H
+#define MPI_STATS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// The VMPI stats module reports a bunch of statistics to a MySQL server, and the
+// stats can be used to trace and graph a compile session.
+//
+
+// Call this as soon as possible to initialize spew hooks.
+void VMPI_Stats_InstallSpewHook();
+
+//
+// pDBServerName is the hostname (or dotted IP address) of the MySQL server to connect to.
+// pBSPFilename is the last argument on the command line.
+// pMachineIP is the dotted IP address of this machine.
+// jobID is an 8-byte unique identifier for this job.
+//
+bool VMPI_Stats_Init_Master( const char *pHostName, const char *pDBName, const char *pUserName, const char *pBSPFilename, unsigned long *pDBJobID );
+bool VMPI_Stats_Init_Worker( const char *pHostName, const char *pDBName, const char *pUserName, unsigned long DBJobID );
+void VMPI_Stats_Term();
+
+// Add a generic text event to the database.
+void VMPI_Stats_AddEventText( const char *pText );
+
+class CDBInfo
+{
+public:
+ char m_HostName[128];
+ char m_DBName[128];
+ char m_UserName[128];
+};
+
+// If you're the master, this loads pDBInfoFilename, sends that info to the workers, and
+// connects to the database.
+//
+// If you're a worker, this waits for the DB info, then connects to the database.
+void StatsDB_InitStatsDatabase(
+ int argc,
+ char **argv,
+ const char *pDBInfoFilename );
+
+// The database gives back a unique ID for the job.
+unsigned long StatsDB_GetUniqueJobID();
+
+// Get the worker ID (used for the JobWorkerID fields in the database).
+unsigned long VMPI_Stats_GetJobWorkerID();
+
+
+#endif // MPI_STATS_H
diff --git a/mp/src/utils/common/mstristrip.cpp b/mp/src/utils/common/mstristrip.cpp new file mode 100644 index 00000000..6a66de2f --- /dev/null +++ b/mp/src/utils/common/mstristrip.cpp @@ -0,0 +1,930 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//-----------------------------------------------------------------------------
+// FILE: TRISTRIP.CPP
+//
+// Desc: Xbox tristripper
+//
+// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+// identifier was truncated to '255' characters in the debug information
+#pragma warning(disable: 4786)
+// conversion from 'double' to 'float'
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4530)
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <algorithm>
+#include <list>
+#include <vector>
+
+#include <assert.h>
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+
+#include "mstristrip.h"
+
+using namespace std;
+
+//=========================================================================
+// structs
+//=========================================================================
+typedef vector<WORD> STRIPVERTS;
+typedef list<STRIPVERTS *> STRIPLIST;
+typedef WORD (*TRIANGLELIST)[3];
+
+struct TRIANGLEINFO
+{
+ int neighbortri[3];
+ int neighboredge[3];
+};
+
+// return true if strip starts clockwise
+inline bool FIsStripCW(const STRIPVERTS &stripvertices)
+{
+ // last index should have cw/ccw bool
+ return !!stripvertices[stripvertices.size() - 1];
+}
+
+// return length of strip
+inline int StripLen(const STRIPVERTS &stripvertices)
+{
+ return (int)stripvertices.size() - 1;
+}
+
+// free all stripverts and clear the striplist
+inline void FreeStripListVerts(STRIPLIST *pstriplist)
+{
+ STRIPLIST::iterator istriplist = pstriplist->begin();
+ while(istriplist != pstriplist->end())
+ {
+ STRIPVERTS *pstripverts = *istriplist;
+ delete pstripverts;
+ pstriplist->erase(istriplist++);
+ }
+}
+
+//=========================================================================
+// main stripper class
+//=========================================================================
+class CStripper
+{
+public:
+ // ctors/dtors
+ CStripper(int numtris, TRIANGLELIST ptriangles);
+ ~CStripper();
+
+ // initialize tri info
+ void InitTriangleInfo(int tri, int vert);
+
+ // get maximum length strip from tri/vert
+ int CreateStrip(int tri, int vert, int maxlen, int *pswaps,
+ bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts);
+
+ // stripify entire mesh
+ void BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead);
+
+ // blast strip indices to ppstripindices
+ int CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices);
+ int CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices);
+
+ inline int GetNeighborCount(int tri)
+ {
+ int count = 0;
+ for(int vert = 0; vert < 3; vert++)
+ {
+ int neighbortri = m_ptriinfo[tri].neighbortri[vert];
+ count += (neighbortri != -1) && !m_pused[neighbortri];
+ }
+ return count;
+ }
+
+ // from callee
+ int m_numtris; // # tris
+ TRIANGLELIST m_ptriangles; // trilist
+
+ TRIANGLEINFO *m_ptriinfo; // tri edge, neighbor info
+ int *m_pused; // tri used flag
+};
+
+//=========================================================================
+// vertex cache class
+//=========================================================================
+class CVertCache
+{
+public:
+ CVertCache()
+ { Reset(); }
+ ~CVertCache()
+ {};
+
+ // reset cache
+ void Reset()
+ {
+ m_iCachePtr = 0;
+ m_cachehits = 0;
+ memset(m_rgCache, 0xff, sizeof(m_rgCache));
+ }
+
+ // add vertindex to cache
+ bool Add(int strip, int vertindex);
+
+ int NumCacheHits() const
+ { return m_cachehits; }
+
+// enum { CACHE_SIZE = 10 };
+ enum { CACHE_SIZE = 18 };
+
+private:
+ int m_cachehits; // current # of cache hits
+ WORD m_rgCache[CACHE_SIZE]; // vertex cache
+ int m_rgCacheStrip[CACHE_SIZE]; // strip # which added vert
+ int m_iCachePtr; // fifo ptr
+};
+
+//=========================================================================
+// Get maximum length of strip starting at tri/vert
+//=========================================================================
+int CStripper::CreateStrip(int tri, int vert, int maxlen, int *pswaps,
+ bool flookahead, bool fstartcw, int *pstriptris, int *pstripverts)
+{
+ *pswaps = 0;
+
+ // this guy has already been used?
+ if(m_pused[tri])
+ return 0;
+
+ // mark tri as used
+ m_pused[tri] = 1;
+
+ int swaps = 0;
+
+ // add first tri info
+ pstriptris[0] = tri;
+ pstriptris[1] = tri;
+ pstriptris[2] = tri;
+
+ if(fstartcw)
+ {
+ pstripverts[0] = (vert) % 3;
+ pstripverts[1] = (vert + 1) % 3;
+ pstripverts[2] = (vert + 2) % 3;
+ }
+ else
+ {
+ pstripverts[0] = (vert + 1) % 3;
+ pstripverts[1] = (vert + 0) % 3;
+ pstripverts[2] = (vert + 2) % 3;
+ }
+ fstartcw = !fstartcw;
+
+ // get next tri information
+ int edge = (fstartcw ? vert + 2 : vert + 1) % 3;
+ int nexttri = m_ptriinfo[tri].neighbortri[edge];
+ int nextvert = m_ptriinfo[tri].neighboredge[edge];
+
+ // start building the strip until we run out of room or indices
+ int stripcount;
+ for( stripcount = 3; stripcount < maxlen; stripcount++)
+ {
+ // dead end?
+ if(nexttri == -1 || m_pused[nexttri])
+ break;
+
+ // move to next tri
+ tri = nexttri;
+ vert = nextvert;
+
+ // toggle orientation
+ fstartcw = !fstartcw;
+
+ // find the next natural edge
+ int edge = (fstartcw ? vert + 2 : vert + 1) % 3;
+ nexttri = m_ptriinfo[tri].neighbortri[edge];
+ nextvert = m_ptriinfo[tri].neighboredge[edge];
+
+ bool fswap = false;
+ if(nexttri == -1 || m_pused[nexttri])
+ {
+ // if the next tri is a dead end - try swapping orientation
+ fswap = true;
+ }
+ else if(flookahead)
+ {
+ // try a swap and see who our new neighbor would be
+ int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3;
+ int nexttriswap = m_ptriinfo[tri].neighbortri[edgeswap];
+ int nextvertswap = m_ptriinfo[tri].neighboredge[edgeswap];
+
+ if(nexttriswap != -1 && !m_pused[nexttriswap])
+ {
+ assert(nexttri != -1);
+
+ // if the swap neighbor has a lower count, change directions
+ if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri))
+ {
+ fswap = true;
+ }
+ else if(GetNeighborCount(nexttriswap) == GetNeighborCount(nexttri))
+ {
+ // if they have the same number of neighbors - check their neighbors
+ edgeswap = (fstartcw ? nextvertswap + 2 : nextvertswap + 1) % 3;
+ nexttriswap = m_ptriinfo[nexttriswap].neighbortri[edgeswap];
+
+ int edge1 = (fstartcw ? nextvert + 1 : nextvert + 2) % 3;
+ int nexttri1 = m_ptriinfo[nexttri].neighbortri[edge1];
+
+ if(nexttri1 == -1 || m_pused[nexttri1])
+ {
+ // natural winding order leads us to a dead end so turn
+ fswap = true;
+ }
+ else if(nexttriswap != -1 && !m_pused[nexttriswap])
+ {
+ // check neighbor counts on both directions and swap if it's better
+ if(GetNeighborCount(nexttriswap) < GetNeighborCount(nexttri1))
+ fswap = true;
+ }
+ }
+ }
+ }
+
+ if(fswap)
+ {
+ // we've been told to change directions so make sure we actually can
+ // and then add the swap vertex
+ int edgeswap = (fstartcw ? vert + 1 : vert + 2) % 3;
+ nexttri = m_ptriinfo[tri].neighbortri[edgeswap];
+ nextvert = m_ptriinfo[tri].neighboredge[edgeswap];
+
+ if(nexttri != -1 && !m_pused[nexttri])
+ {
+ pstriptris[stripcount] = pstriptris[stripcount - 2];
+ pstripverts[stripcount] = pstripverts[stripcount - 2];
+ stripcount++;
+ swaps++;
+ fstartcw = !fstartcw;
+ }
+ }
+
+ // record index information
+ pstriptris[stripcount] = tri;
+ pstripverts[stripcount] = (vert + 2) % 3;
+
+ // mark triangle as used
+ m_pused[tri] = 1;
+ }
+
+ // clear the used flags
+ for(int j = 2; j < stripcount; j++)
+ m_pused[pstriptris[j]] = 0;
+
+ // return swap count and striplen
+ *pswaps = swaps;
+ return stripcount;
+}
+
+//=========================================================================
+// Given a striplist and current cache state, pick the best next strip
+//=========================================================================
+STRIPLIST::iterator FindBestCachedStrip(STRIPLIST *pstriplist,
+ const CVertCache &vertcachestate)
+{
+ if(pstriplist->empty())
+ return pstriplist->end();
+
+ bool fFlipStrip = false;
+ int maxcachehits = -1;
+ STRIPLIST::iterator istriplistbest = pstriplist->begin();
+
+ int striplen = StripLen(**istriplistbest);
+ bool fstartcw = FIsStripCW(**istriplistbest);
+
+ // go through all the other strips looking for the best caching
+ for(STRIPLIST::iterator istriplist = pstriplist->begin();
+ istriplist != pstriplist->end();
+ ++istriplist)
+ {
+ bool fFlip = false;
+ const STRIPVERTS &stripverts = **istriplist;
+ int striplennew = StripLen(stripverts);
+
+ // check cache if this strip is the same type as us (ie: cw/odd)
+ if((FIsStripCW(stripverts) == fstartcw) &&
+ ((striplen & 0x1) == (striplennew & 0x1)))
+ {
+ // copy current state of cache
+ CVertCache vertcachenew = vertcachestate;
+
+ // figure out what this guy would do to our cache
+ for(int ivert = 0; ivert < striplennew; ivert++)
+ vertcachenew.Add(2, stripverts[ivert]);
+
+ // even length strip - see if better cache hits reversed
+ if(!(striplennew & 0x1))
+ {
+ CVertCache vertcacheflipped = vertcachestate;
+
+ for(int ivert = StripLen(stripverts) - 1; ivert >= 0; ivert--)
+ vertcacheflipped.Add(2, stripverts[ivert]);
+
+ if(vertcacheflipped.NumCacheHits() > vertcachenew.NumCacheHits())
+ {
+ vertcachenew = vertcacheflipped;
+ fFlip = true;
+ }
+ }
+
+ // record the best number of cache hits to date
+ int numcachehits = vertcachenew.NumCacheHits() - vertcachestate.NumCacheHits();
+ if(numcachehits > maxcachehits)
+ {
+ maxcachehits = numcachehits;
+ istriplistbest = istriplist;
+ fFlipStrip = fFlip;
+ }
+ }
+ }
+
+ if(fFlipStrip)
+ {
+ STRIPVERTS &stripverts = **istriplistbest;
+ STRIPVERTS::iterator vend = stripverts.end();
+
+ reverse(stripverts.begin(), --vend);
+ }
+
+ // make sure we keep the list in order and always pull off
+ // the first dude.
+ if(istriplistbest != pstriplist->begin())
+ swap(*istriplistbest, *pstriplist->begin());
+
+ return pstriplist->begin();
+}
+
+
+//=========================================================================
+// Don't merge the strips - just blast em into the stripbuffer one by one
+// (useful for debugging)
+//=========================================================================
+int CStripper::CreateManyStrips(STRIPLIST *pstriplist, WORD **ppstripindices)
+{
+ // allow room for each of the strips size plus the final 0
+ int indexcount = (int)pstriplist->size() + 1;
+
+ // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format
+ STRIPLIST::iterator istriplist;
+ for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist)
+ {
+ // add striplength plus potential degenerate to swap ccw --> cw
+ indexcount += StripLen(**istriplist) + 1;
+ }
+
+ // alloc the space for all this stuff
+ WORD *pstripindices = new WORD [indexcount];
+ assert(pstripindices);
+
+ CVertCache vertcache;
+ int numstripindices = 0;
+
+ for(istriplist = pstriplist->begin();
+ !pstriplist->empty();
+ istriplist = FindBestCachedStrip(pstriplist, vertcache))
+ {
+ const STRIPVERTS &stripverts = **istriplist;
+
+ if(!FIsStripCW(stripverts))
+ {
+ // add an extra index if it's ccw
+ pstripindices[numstripindices++] = StripLen(stripverts) + 1;
+ pstripindices[numstripindices++] = stripverts[0];
+ }
+ else
+ {
+ // add the strip length
+ pstripindices[numstripindices++] = StripLen(stripverts);
+ }
+
+ // add all the strip indices
+ for(int i = 0; i < StripLen(stripverts); i++)
+ {
+ pstripindices[numstripindices++] = stripverts[i];
+ vertcache.Add(1, stripverts[i]);
+ }
+
+ // free this guy and pop him off the list
+ delete &stripverts;
+ pstriplist->pop_front();
+ }
+
+ // add terminating zero
+ pstripindices[numstripindices++] = 0;
+ *ppstripindices = pstripindices;
+
+ return numstripindices;
+}
+
+//=========================================================================
+// Merge striplist into one big uberlist with (hopefully) optimal caching
+//=========================================================================
+int CStripper::CreateLongStrip(STRIPLIST *pstriplist, WORD **ppstripindices)
+{
+ // allow room for one strip length plus a possible 3 extra indices per
+ // concatenated strip list plus the final 0
+ int indexcount = ((int)pstriplist->size() * 3) + 2;
+
+ // we're storing the strips in [size1 i1 i2 i3][size2 i4 i5 i6][0] format
+ STRIPLIST::iterator istriplist;
+ for( istriplist = pstriplist->begin(); istriplist != pstriplist->end(); ++istriplist)
+ {
+ indexcount += StripLen(**istriplist);
+ }
+
+ // alloc the space for all this stuff
+ WORD *pstripindices = new WORD [indexcount];
+ assert(pstripindices);
+
+ CVertCache vertcache;
+ int numstripindices = 0;
+
+ // add first strip
+ istriplist = pstriplist->begin();
+ const STRIPVERTS &stripverts = **istriplist;
+
+ // first strip should be cw
+ assert(FIsStripCW(stripverts));
+
+ for(int ivert = 0; ivert < StripLen(stripverts); ivert++)
+ {
+ pstripindices[numstripindices++] = stripverts[ivert];
+ vertcache.Add(1, stripverts[ivert]);
+ }
+
+ // kill first dude
+ delete &stripverts;
+ pstriplist->erase(istriplist);
+
+ // add all the others
+ while(pstriplist->size())
+ {
+ istriplist = FindBestCachedStrip(pstriplist, vertcache);
+ STRIPVERTS &stripverts = **istriplist;
+ short lastvert = pstripindices[numstripindices - 1];
+ short firstvert = stripverts[0];
+
+ if(firstvert != lastvert)
+ {
+ // add degenerate from last strip
+ pstripindices[numstripindices++] = lastvert;
+
+ // add degenerate from our strip
+ pstripindices[numstripindices++] = firstvert;
+ }
+
+ // if we're not orientated correctly, we need to add a degenerate
+ if(FIsStripCW(stripverts) != !(numstripindices & 0x1))
+ {
+ // This shouldn't happen - we're currently trying very hard
+ // to keep everything oriented correctly.
+ assert(false);
+ pstripindices[numstripindices++] = firstvert;
+ }
+
+ // add these verts
+ for(int ivert = 0; ivert < StripLen(stripverts); ivert++)
+ {
+ pstripindices[numstripindices++] = stripverts[ivert];
+ vertcache.Add(1, stripverts[ivert]);
+ }
+
+ // free these guys
+ delete &stripverts;
+ pstriplist->erase(istriplist);
+ }
+
+ *ppstripindices = pstripindices;
+ return numstripindices;
+}
+
+//=========================================================================
+// Build a (hopefully) optimal set of strips from a trilist
+//=========================================================================
+void CStripper::BuildStrips(STRIPLIST *pstriplist, int maxlen, bool flookahead)
+{
+ // temp indices storage
+ const int ctmpverts = 1024;
+ int pstripverts[ctmpverts + 1];
+ int pstriptris[ctmpverts + 1];
+
+ assert(maxlen <= ctmpverts);
+
+ // clear all the used flags for the tris
+ memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);
+
+ bool fstartcw = true;
+ for(;;)
+ {
+ int besttri = 0;
+ int bestvert = 0;
+ float bestratio = 2.0f;
+ int bestneighborcount = INT_MAX;
+
+ int tri;
+ for( tri = 0; tri < m_numtris; tri++)
+ {
+ // if used the continue
+ if(m_pused[tri])
+ continue;
+
+ // get the neighbor count
+ int curneightborcount = GetNeighborCount(tri);
+ assert(curneightborcount >= 0 && curneightborcount <= 3);
+
+ // push all the singletons to the very end
+ if(!curneightborcount)
+ curneightborcount = 4;
+
+ // if this guy has more neighbors than the current best - bail
+ if(curneightborcount > bestneighborcount)
+ continue;
+
+ // try starting the strip with each of this tris verts
+ for(int vert = 0; vert < 3; vert++)
+ {
+ int swaps;
+ int len = CreateStrip(tri, vert, maxlen, &swaps, flookahead,
+ fstartcw, pstriptris, pstripverts);
+ assert(len);
+
+ float ratio = (len == 3) ? 1.0f : (float)swaps / len;
+
+ // check if this ratio is better than what we've already got for
+ // this neighborcount
+ if((curneightborcount < bestneighborcount) ||
+ ((curneightborcount == bestneighborcount) && (ratio < bestratio)))
+ {
+ bestneighborcount = curneightborcount;
+
+ besttri = tri;
+ bestvert = vert;
+ bestratio = ratio;
+ }
+
+ }
+ }
+
+ // no strips found?
+ if(bestneighborcount == INT_MAX)
+ break;
+
+ // recreate this strip
+ int swaps;
+ int len = CreateStrip(besttri, bestvert, maxlen,
+ &swaps, flookahead, fstartcw, pstriptris, pstripverts);
+ assert(len);
+
+ // mark the tris on the best strip as used
+ for(tri = 0; tri < len; tri++)
+ m_pused[pstriptris[tri]] = 1;
+
+ // create a new STRIPVERTS and stuff in the indices
+ STRIPVERTS *pstripvertices = new STRIPVERTS(len + 1);
+ assert(pstripvertices);
+
+ // store orientation in first entry
+ for(tri = 0; tri < len; tri++)
+ (*pstripvertices)[tri] = m_ptriangles[pstriptris[tri]][pstripverts[tri]];
+ (*pstripvertices)[len] = fstartcw;
+
+ // store the STRIPVERTS
+ pstriplist->push_back(pstripvertices);
+
+ // if strip was odd - swap orientation
+ if((len & 0x1))
+ fstartcw = !fstartcw;
+ }
+
+#ifdef _DEBUG
+ // make sure all tris are used
+ for(int t = 0; t < m_numtris; t++)
+ assert(m_pused[t]);
+#endif
+}
+
+//=========================================================================
+// Guesstimate on the total index count for this list of strips
+//=========================================================================
+int EstimateStripCost(STRIPLIST *pstriplist)
+{
+ int count = 0;
+
+ for(STRIPLIST::iterator istriplist = pstriplist->begin();
+ istriplist != pstriplist->end();
+ ++istriplist)
+ {
+ // add count of indices
+ count += StripLen(**istriplist);
+ }
+
+ // assume 2 indices per strip to tack all these guys together
+ return count + ((int)pstriplist->size() - 1) * 2;
+}
+
+//=========================================================================
+// Initialize triangle information (edges, #neighbors, etc.)
+//=========================================================================
+void CStripper::InitTriangleInfo(int tri, int vert)
+{
+ WORD *ptriverts = &m_ptriangles[tri + 1][0];
+ int vert1 = m_ptriangles[tri][(vert + 1) % 3];
+ int vert2 = m_ptriangles[tri][vert];
+
+ for(int itri = tri + 1; itri < m_numtris; itri++, ptriverts += 3)
+ {
+ if(m_pused[itri] != 0x7)
+ {
+ for(int ivert = 0; ivert < 3; ivert++)
+ {
+ if((ptriverts[ivert] == vert1) &&
+ (ptriverts[(ivert + 1) % 3] == vert2))
+ {
+ // add the triangle info
+ m_ptriinfo[tri].neighbortri[vert] = itri;
+ m_ptriinfo[tri].neighboredge[vert] = ivert;
+ m_pused[tri] |= (1 << vert);
+
+ m_ptriinfo[itri].neighbortri[ivert] = tri;
+ m_ptriinfo[itri].neighboredge[ivert] = vert;
+ m_pused[itri] |= (1 << ivert);
+ return;
+ }
+ }
+ }
+ }
+}
+
+//=========================================================================
+// CStripper ctor
+//=========================================================================
+CStripper::CStripper(int numtris, TRIANGLELIST ptriangles)
+{
+ // store trilist info
+ m_numtris = numtris;
+ m_ptriangles = ptriangles;
+
+ m_pused = new int[numtris];
+ assert(m_pused);
+ m_ptriinfo = new TRIANGLEINFO[numtris];
+ assert(m_ptriinfo);
+
+ // init triinfo
+ int itri;
+ for( itri = 0; itri < numtris; itri++)
+ {
+ m_ptriinfo[itri].neighbortri[0] = -1;
+ m_ptriinfo[itri].neighbortri[1] = -1;
+ m_ptriinfo[itri].neighbortri[2] = -1;
+ }
+
+ // clear the used flag
+ memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);
+
+ // go through all the triangles and find edges, neighbor counts
+ for(itri = 0; itri < numtris; itri++)
+ {
+ for(int ivert = 0; ivert < 3; ivert++)
+ {
+ if(!(m_pused[itri] & (1 << ivert)))
+ InitTriangleInfo(itri, ivert);
+ }
+ }
+
+ // clear the used flags from InitTriangleInfo
+ memset(m_pused, 0, sizeof(m_pused[0]) * m_numtris);
+}
+
+//=========================================================================
+// CStripper dtor
+//=========================================================================
+CStripper::~CStripper()
+{
+ // free stuff
+ delete [] m_pused;
+ m_pused = NULL;
+
+ delete [] m_ptriinfo;
+ m_ptriinfo = NULL;
+}
+
+//=========================================================================
+// Add an index to the cache - returns true if it was added, false otherwise
+//=========================================================================
+bool CVertCache::Add(int strip, int vertindex)
+{
+ // find index in cache
+ for(int iCache = 0; iCache < CACHE_SIZE; iCache++)
+ {
+ if(vertindex == m_rgCache[iCache])
+ {
+ // if it's in the cache and it's from a different strip
+ // change the strip to the new one and count the cache hit
+ if(strip != m_rgCacheStrip[iCache])
+ {
+ m_cachehits++;
+ m_rgCacheStrip[iCache] = strip;
+ return true;
+ }
+
+ // we added this item to the cache earlier - carry on
+ return false;
+ }
+ }
+
+ // not in cache, add vert and strip
+ m_rgCache[m_iCachePtr] = vertindex;
+ m_rgCacheStrip[m_iCachePtr] = strip;
+ m_iCachePtr = (m_iCachePtr + 1) % CACHE_SIZE;
+ return true;
+}
+
+#ifdef _DEBUG
+//=========================================================================
+// Turn on c runtime leak checking, etc.
+//=========================================================================
+void EnableLeakChecking()
+{
+ int flCrtDbgFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+
+ flCrtDbgFlags &=
+ ~(_CRTDBG_LEAK_CHECK_DF |
+ _CRTDBG_CHECK_ALWAYS_DF |
+ _CRTDBG_DELAY_FREE_MEM_DF);
+
+ // always check for memory leaks
+ flCrtDbgFlags |= _CRTDBG_LEAK_CHECK_DF;
+
+ // others you may / may not want to set
+ flCrtDbgFlags |= _CRTDBG_CHECK_ALWAYS_DF;
+ flCrtDbgFlags |= _CRTDBG_DELAY_FREE_MEM_DF;
+
+ _CrtSetDbgFlag(flCrtDbgFlags);
+
+ // all types of reports go via OutputDebugString
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
+
+ // big errors and asserts get their own assert window
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW);
+
+ // _CrtSetBreakAlloc(0);
+}
+#endif
+
+//=========================================================================
+// Main Stripify routine
+//=========================================================================
+int Stripify(int numtris, WORD *ptriangles, int *pnumindices, WORD **ppstripindices)
+{
+ if(!numtris || !ptriangles)
+ return 0;
+
+#ifdef _DEBUG
+// EnableLeakChecking();
+#endif
+
+ CStripper stripper(numtris, (TRIANGLELIST)ptriangles);
+
+ // map of various args to try stripifying mesh with
+ struct ARGMAP
+ {
+ int maxlen; // maximum length of strips
+ bool flookahead; // use sgi greedy lookahead (or not)
+ } rgargmap[] =
+ {
+ { 1024, true },
+ { 1024, false },
+ };
+ static const int cargmaps = sizeof(rgargmap) / sizeof(rgargmap[0]);
+ STRIPLIST striplistbest;
+ int bestlistcost = 0;
+
+ for(int imap = 0; imap < cargmaps; imap++)
+ {
+ STRIPLIST striplist;
+
+ // build the strip with the various args
+ stripper.BuildStrips(&striplist, rgargmap[imap].maxlen,
+ rgargmap[imap].flookahead);
+
+ // guesstimate the list cost and store it if it's good
+ int listcost = EstimateStripCost(&striplist);
+ if(!bestlistcost || (listcost < bestlistcost))
+ {
+ // free the old best list
+ FreeStripListVerts(&striplistbest);
+
+ // store the new best list
+ striplistbest = striplist;
+ bestlistcost = listcost;
+ assert(bestlistcost > 0);
+ }
+ else
+ {
+ FreeStripListVerts(&striplist);
+ }
+ }
+
+#ifdef NEVER
+ // Return the strips in [size1 i1 i2 i3][size2 i4 i5 i6]...[0] format
+ // Very useful for debugging...
+ return stripper.CreateManyStrips(&striplistbest, ppstripindices);
+#endif // NEVER
+
+ // return one big long strip
+ int numindices = stripper.CreateLongStrip(&striplistbest, ppstripindices);
+
+ if(pnumindices)
+ *pnumindices = numindices;
+ return numindices;
+}
+
+//=========================================================================
+// Class used to vertices for locality of access.
+//=========================================================================
+struct SortEntry
+{
+public:
+ int iFirstUsed;
+ int iOrigIndex;
+
+ bool operator<(const SortEntry& rhs)
+ {
+ return iFirstUsed < rhs.iFirstUsed;
+ }
+};
+
+//=========================================================================
+// Reorder the vertices
+//=========================================================================
+void ComputeVertexPermutation(int numstripindices, WORD* pstripindices,
+ int* pnumverts, WORD** ppvertexpermutation)
+{
+ // Sort verts to maximize locality.
+ SortEntry* pSortTable = new SortEntry[*pnumverts];
+
+ // Fill in original index.
+ int i;
+ for( i = 0; i < *pnumverts; i++)
+ {
+ pSortTable[i].iOrigIndex = i;
+ pSortTable[i].iFirstUsed = -1;
+ }
+
+ // Fill in first used flag.
+ for(i = 0; i < numstripindices; i++)
+ {
+ int index = pstripindices[i];
+
+ if(pSortTable[index].iFirstUsed == -1)
+ pSortTable[index].iFirstUsed = i;
+ }
+
+ // Sort the table.
+ sort(pSortTable, pSortTable + *pnumverts);
+
+ // Copy re-mapped to orignal vertex permutaion into output array.
+ *ppvertexpermutation = new WORD[*pnumverts];
+
+ for(i = 0; i < *pnumverts; i++)
+ {
+ (*ppvertexpermutation)[i] = pSortTable[i].iOrigIndex;
+ }
+
+ // Build original to re-mapped permutation.
+ WORD* pInversePermutation = new WORD[numstripindices];
+
+ for(i = 0; i < *pnumverts; i++)
+ {
+ pInversePermutation[pSortTable[i].iOrigIndex] = i;
+ }
+
+ // We need to remap indices as well.
+ for(i = 0; i < numstripindices; i++)
+ {
+ pstripindices[i] = pInversePermutation[pstripindices[i]];
+ }
+
+ delete[] pSortTable;
+ delete[] pInversePermutation;
+}
+
diff --git a/mp/src/utils/common/mstristrip.h b/mp/src/utils/common/mstristrip.h new file mode 100644 index 00000000..626a0062 --- /dev/null +++ b/mp/src/utils/common/mstristrip.h @@ -0,0 +1,43 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//-----------------------------------------------------------------------------
+// FILE: TRISTRIP.H
+//
+// Desc: tristrip header file
+//
+// Copyright (c) 1999-2000 Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+typedef unsigned short WORD;
+
+//
+// Main Stripify routine. Returns number of strip indices contained
+// in ppstripindices. Caller must delete [] ppstripindices.
+//
+int Stripify(
+ int numtris, // Number of triangles
+ WORD *ptriangles, // triangle indices pointer
+ int *pnumindices, // number of indices in ppstripindices (out)
+ WORD **ppstripindices // triangle strip indices
+);
+
+//
+// Re-arrange vertices so that they occur in the order that they are first
+// used. This function doesn't actually move vertex data around, it returns
+// an array that specifies where in the new vertex array each old vertex
+// should go. It also re-maps the strip indices to use the new vertex
+// locations. Caller must delete [] pVertexPermutation.
+//
+void ComputeVertexPermutation
+(
+ int numstripindices, // Number of strip indices
+ WORD *pstripindices, // Strip indices
+ int *pnumverts, // Number of verts (in and out)
+ WORD **ppvertexpermutation // Map from orignal index to remapped index
+);
+
diff --git a/mp/src/utils/common/pacifier.cpp b/mp/src/utils/common/pacifier.cpp new file mode 100644 index 00000000..6d9c73f2 --- /dev/null +++ b/mp/src/utils/common/pacifier.cpp @@ -0,0 +1,63 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+#include "basetypes.h"
+#include "pacifier.h"
+#include "tier0/dbg.h"
+
+
+static int g_LastPacifierDrawn = -1;
+static bool g_bPacifierSuppressed = false;
+
+#define clamp(a,b,c) ( (a) > (c) ? (c) : ( (a) < (b) ? (b) : (a) ) )
+
+void StartPacifier( char const *pPrefix )
+{
+ Msg( "%s", pPrefix );
+ g_LastPacifierDrawn = -1;
+ UpdatePacifier( 0.001f );
+}
+
+void UpdatePacifier( float flPercent )
+{
+ int iCur = (int)(flPercent * 40.0f);
+ iCur = clamp( iCur, g_LastPacifierDrawn, 40 );
+
+ if( iCur != g_LastPacifierDrawn && !g_bPacifierSuppressed )
+ {
+ for( int i=g_LastPacifierDrawn+1; i <= iCur; i++ )
+ {
+ if ( !( i % 4 ) )
+ {
+ Msg("%d", i/4);
+ }
+ else
+ {
+ if( i != 40 )
+ {
+ Msg(".");
+ }
+ }
+ }
+
+ g_LastPacifierDrawn = iCur;
+ }
+}
+
+void EndPacifier( bool bCarriageReturn )
+{
+ UpdatePacifier(1);
+
+ if( bCarriageReturn && !g_bPacifierSuppressed )
+ Msg("\n");
+}
+
+void SuppressPacifier( bool bSuppress )
+{
+ g_bPacifierSuppressed = bSuppress;
+}
diff --git a/mp/src/utils/common/pacifier.h b/mp/src/utils/common/pacifier.h new file mode 100644 index 00000000..42ecd6ec --- /dev/null +++ b/mp/src/utils/common/pacifier.h @@ -0,0 +1,23 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PACIFIER_H
+#define PACIFIER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// Use these to display a pacifier like:
+// ProcessBlock_Thread: 0...1...2...3...4...5...6...7...8...9... (0)
+void StartPacifier( char const *pPrefix ); // Prints the prefix and resets the pacifier
+void UpdatePacifier( float flPercent ); // percent value between 0 and 1.
+void EndPacifier( bool bCarriageReturn = true ); // Completes pacifier as if 100% was done
+void SuppressPacifier( bool bSuppress = true ); // Suppresses pacifier updates if another thread might still be firing them
+
+
+#endif // PACIFIER_H
diff --git a/mp/src/utils/common/physdll.cpp b/mp/src/utils/common/physdll.cpp new file mode 100644 index 00000000..fae9810a --- /dev/null +++ b/mp/src/utils/common/physdll.cpp @@ -0,0 +1,31 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <stdio.h>
+#include "physdll.h"
+#include "filesystem_tools.h"
+
+static CSysModule *pPhysicsModule = NULL;
+CreateInterfaceFn GetPhysicsFactory( void )
+{
+ if ( !pPhysicsModule )
+ {
+ pPhysicsModule = g_pFullFileSystem->LoadModule( "VPHYSICS.DLL" );
+ if ( !pPhysicsModule )
+ return NULL;
+ }
+
+ return Sys_GetFactory( pPhysicsModule );
+}
+
+void PhysicsDLLPath( const char *pPathname )
+{
+ if ( !pPhysicsModule )
+ {
+ pPhysicsModule = g_pFullFileSystem->LoadModule( pPathname );
+ }
+}
diff --git a/mp/src/utils/common/physdll.h b/mp/src/utils/common/physdll.h new file mode 100644 index 00000000..151a9e50 --- /dev/null +++ b/mp/src/utils/common/physdll.h @@ -0,0 +1,30 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PHYSDLL_H
+#define PHYSDLL_H
+#pragma once
+
+
+#ifdef __cplusplus
+#include "vphysics_interface.h"
+class IPhysics;
+class IPhysicsCollision;
+
+extern CreateInterfaceFn GetPhysicsFactory( void );
+
+extern "C" {
+#endif
+
+// tools need to force the path
+void PhysicsDLLPath( const char *pPathname );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // PHYSDLL_H
diff --git a/mp/src/utils/common/polylib.cpp b/mp/src/utils/common/polylib.cpp new file mode 100644 index 00000000..36690a27 --- /dev/null +++ b/mp/src/utils/common/polylib.cpp @@ -0,0 +1,915 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "polylib.h"
+#include "worldsize.h"
+#include "threads.h"
+#include "tier0/dbg.h"
+
+// doesn't seem to need to be here? -- in threads.h
+//extern int numthreads;
+
+// counters are only bumped when running single threaded,
+// because they are an awefull coherence problem
+int c_active_windings;
+int c_peak_windings;
+int c_winding_allocs;
+int c_winding_points;
+
+void pw(winding_t *w)
+{
+ int i;
+ for (i=0 ; i<w->numpoints ; i++)
+ printf ("(%5.1f, %5.1f, %5.1f)\n",w->p[i][0], w->p[i][1],w->p[i][2]);
+}
+
+winding_t *winding_pool[MAX_POINTS_ON_WINDING+4];
+
+/*
+=============
+AllocWinding
+=============
+*/
+winding_t *AllocWinding (int points)
+{
+ winding_t *w;
+
+ if (numthreads == 1)
+ {
+ c_winding_allocs++;
+ c_winding_points += points;
+ c_active_windings++;
+ if (c_active_windings > c_peak_windings)
+ c_peak_windings = c_active_windings;
+ }
+ ThreadLock();
+ if (winding_pool[points])
+ {
+ w = winding_pool[points];
+ winding_pool[points] = w->next;
+ }
+ else
+ {
+ w = (winding_t *)malloc(sizeof(*w));
+ w->p = (Vector *)calloc( points, sizeof(Vector) );
+ }
+ ThreadUnlock();
+ w->numpoints = 0; // None are occupied yet even though allocated.
+ w->maxpoints = points;
+ w->next = NULL;
+ return w;
+}
+
+void FreeWinding (winding_t *w)
+{
+ if (w->numpoints == 0xdeaddead)
+ Error ("FreeWinding: freed a freed winding");
+
+ ThreadLock();
+ w->numpoints = 0xdeaddead; // flag as freed
+ w->next = winding_pool[w->maxpoints];
+ winding_pool[w->maxpoints] = w;
+ ThreadUnlock();
+}
+
+/*
+============
+RemoveColinearPoints
+============
+*/
+int c_removed;
+
+void RemoveColinearPoints (winding_t *w)
+{
+ int i, j, k;
+ Vector v1, v2;
+ int nump;
+ Vector p[MAX_POINTS_ON_WINDING];
+
+ nump = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = (i+1)%w->numpoints;
+ k = (i+w->numpoints-1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[i], v1);
+ VectorSubtract (w->p[i], w->p[k], v2);
+ VectorNormalize(v1);
+ VectorNormalize(v2);
+ if (DotProduct(v1, v2) < 0.999)
+ {
+ VectorCopy (w->p[i], p[nump]);
+ nump++;
+ }
+ }
+
+ if (nump == w->numpoints)
+ return;
+
+ if (numthreads == 1)
+ c_removed += w->numpoints - nump;
+ w->numpoints = nump;
+ memcpy (w->p, p, nump*sizeof(p[0]));
+}
+
+/*
+============
+WindingPlane
+============
+*/
+void WindingPlane (winding_t *w, Vector &normal, vec_t *dist)
+{
+ Vector v1, v2;
+
+ VectorSubtract (w->p[1], w->p[0], v1);
+
+ // HACKHACK: Avoid potentially collinear verts
+ if ( w->numpoints > 3 )
+ {
+ VectorSubtract (w->p[3], w->p[0], v2);
+ }
+ else
+ {
+ VectorSubtract (w->p[2], w->p[0], v2);
+ }
+ CrossProduct (v2, v1, normal);
+ VectorNormalize (normal);
+ *dist = DotProduct (w->p[0], normal);
+
+}
+
+
+/*
+=============
+WindingArea
+=============
+*/
+vec_t WindingArea(winding_t *w)
+{
+ int i;
+ Vector d1, d2, cross;
+ vec_t total;
+
+ total = 0;
+ for (i=2 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->p[i-1], w->p[0], d1);
+ VectorSubtract (w->p[i], w->p[0], d2);
+ CrossProduct (d1, d2, cross);
+ total += VectorLength ( cross );
+ }
+ return total * 0.5;
+}
+
+void WindingBounds (winding_t *w, Vector &mins, Vector &maxs)
+{
+ vec_t v;
+ int i,j;
+
+ mins[0] = mins[1] = mins[2] = 99999;
+ maxs[0] = maxs[1] = maxs[2] = -99999;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ v = w->p[i][j];
+ if (v < mins[j])
+ mins[j] = v;
+ if (v > maxs[j])
+ maxs[j] = v;
+ }
+ }
+}
+
+/*
+=============
+WindingCenter
+=============
+*/
+void WindingCenter (winding_t *w, Vector ¢er)
+{
+ int i;
+ float scale;
+
+ VectorCopy (vec3_origin, center);
+ for (i=0 ; i<w->numpoints ; i++)
+ VectorAdd (w->p[i], center, center);
+
+ scale = 1.0/w->numpoints;
+ VectorScale (center, scale, center);
+}
+
+
+
+/*
+=============
+WindingCenter
+=============
+*/
+vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er )
+{
+ int i;
+ Vector d1, d2, cross;
+ vec_t total;
+
+ VectorCopy (vec3_origin, center);
+ if ( !w )
+ return 0.0f;
+
+ total = 0;
+ for (i=2 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->p[i-1], w->p[0], d1);
+ VectorSubtract (w->p[i], w->p[0], d2);
+ CrossProduct (d1, d2, cross);
+ float area = VectorLength ( cross );
+ total += area;
+
+ // center of triangle, weighed by area
+ VectorMA( center, area / 3.0, w->p[i-1], center );
+ VectorMA( center, area / 3.0, w->p[i], center );
+ VectorMA( center, area / 3.0, w->p[0], center );
+ }
+ if (total)
+ {
+ VectorScale( center, 1.0 / total, center );
+ }
+ return total * 0.5;
+}
+
+/*
+=================
+BaseWindingForPlane
+=================
+*/
+winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist)
+{
+ int i, x;
+ vec_t max, v;
+ Vector org, vright, vup;
+ winding_t *w;
+
+// find the major axis
+
+ max = -1;
+ x = -1;
+ for (i=0 ; i<3; i++)
+ {
+ v = fabs(normal[i]);
+ if (v > max)
+ {
+ x = i;
+ max = v;
+ }
+ }
+ if (x==-1)
+ Error ("BaseWindingForPlane: no axis found");
+
+ VectorCopy (vec3_origin, vup);
+ switch (x)
+ {
+ case 0:
+ case 1:
+ vup[2] = 1;
+ break;
+ case 2:
+ vup[0] = 1;
+ break;
+ }
+
+ v = DotProduct (vup, normal);
+ VectorMA (vup, -v, normal, vup);
+ VectorNormalize (vup);
+
+ VectorScale (normal, dist, org);
+
+ CrossProduct (vup, normal, vright);
+
+ VectorScale (vup, (MAX_COORD_INTEGER*4), vup);
+ VectorScale (vright, (MAX_COORD_INTEGER*4), vright);
+
+// project a really big axis aligned box onto the plane
+ w = AllocWinding (4);
+
+ VectorSubtract (org, vright, w->p[0]);
+ VectorAdd (w->p[0], vup, w->p[0]);
+
+ VectorAdd (org, vright, w->p[1]);
+ VectorAdd (w->p[1], vup, w->p[1]);
+
+ VectorAdd (org, vright, w->p[2]);
+ VectorSubtract (w->p[2], vup, w->p[2]);
+
+ VectorSubtract (org, vright, w->p[3]);
+ VectorSubtract (w->p[3], vup, w->p[3]);
+
+ w->numpoints = 4;
+
+ return w;
+}
+
+/*
+==================
+CopyWinding
+==================
+*/
+winding_t *CopyWinding (winding_t *w)
+{
+ int size;
+ winding_t *c;
+
+ c = AllocWinding (w->numpoints);
+ c->numpoints = w->numpoints;
+ size = w->numpoints*sizeof(w->p[0]);
+ memcpy (c->p, w->p, size);
+ return c;
+}
+
+/*
+==================
+ReverseWinding
+==================
+*/
+winding_t *ReverseWinding (winding_t *w)
+{
+ int i;
+ winding_t *c;
+
+ c = AllocWinding (w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorCopy (w->p[w->numpoints-1-i], c->p[i]);
+ }
+ c->numpoints = w->numpoints;
+ return c;
+}
+
+
+// BUGBUG: Hunt this down - it's causing CSG errors
+#pragma optimize("g", off)
+/*
+=============
+ClipWindingEpsilon
+=============
+*/
+
+void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ Vector mid = vec3_origin;
+ winding_t *f, *b;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->p[i], normal);
+ dot -= dist;
+ dists[i] = dot;
+ if (dot > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -epsilon)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ *front = *back = NULL;
+
+ if (!counts[0])
+ {
+ *back = CopyWinding (in);
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = CopyWinding (in);
+ return;
+ }
+
+ maxpts = in->numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ *front = f = AllocWinding (maxpts);
+ *back = b = AllocWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ Vector& p1 = in->p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+ if (sides[i] == SIDE_BACK)
+ {
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ Vector& p2 = in->p[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (normal[j] == 1)
+ mid[j] = dist;
+ else if (normal[j] == -1)
+ mid[j] = -dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (mid, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (f->numpoints > maxpts || b->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+ if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
+ Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+}
+#pragma optimize("", on)
+
+
+// NOTE: This is identical to ClipWindingEpsilon, but it does a pre/post translation to improve precision
+void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset )
+{
+ TranslateWinding( in, offset );
+ ClipWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back );
+ TranslateWinding( in, -offset );
+ if ( front && *front )
+ {
+ TranslateWinding( *front, -offset );
+ }
+ if ( back && *back )
+ {
+ TranslateWinding( *back, -offset );
+ }
+}
+
+void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset)
+{
+ TranslateWinding( in, offset );
+ ClassifyWindingEpsilon( in, normal, dist+DotProduct(offset,normal), epsilon, front, back, on );
+ TranslateWinding( in, -offset );
+ if ( front && *front )
+ {
+ TranslateWinding( *front, -offset );
+ }
+ if ( back && *back )
+ {
+ TranslateWinding( *back, -offset );
+ }
+ if ( on && *on )
+ {
+ TranslateWinding( *on, -offset );
+ }
+}
+
+/*
+=============
+ClassifyWindingEpsilon
+=============
+*/
+// This version returns the winding as "on" if all verts lie in the plane
+void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back, winding_t **on)
+{
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ Vector mid = vec3_origin;
+ winding_t *f, *b;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->p[i], normal);
+ dot -= dist;
+ dists[i] = dot;
+ if (dot > epsilon)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -epsilon)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ *front = *back = *on = NULL;
+
+ if ( !counts[0] && !counts[1] )
+ {
+ *on = CopyWinding(in);
+ return;
+ }
+
+ if (!counts[0])
+ {
+ *back = CopyWinding(in);
+ return;
+ }
+ if (!counts[1])
+ {
+ *front = CopyWinding(in);
+ return;
+ }
+
+ maxpts = in->numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ *front = f = AllocWinding (maxpts);
+ *back = b = AllocWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ Vector& p1 = in->p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+ if (sides[i] == SIDE_BACK)
+ {
+ VectorCopy (p1, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ Vector& p2 = in->p[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (normal[j] == 1)
+ mid[j] = dist;
+ else if (normal[j] == -1)
+ mid[j] = -dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ VectorCopy (mid, b->p[b->numpoints]);
+ b->numpoints++;
+ }
+
+ if (f->numpoints > maxpts || b->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+ if (f->numpoints > MAX_POINTS_ON_WINDING || b->numpoints > MAX_POINTS_ON_WINDING)
+ Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+}
+
+/*
+=============
+ChopWindingInPlace
+=============
+*/
+void ChopWindingInPlace (winding_t **inout, const Vector &normal, vec_t dist, vec_t epsilon)
+{
+ winding_t *in;
+ vec_t dists[MAX_POINTS_ON_WINDING+4];
+ int sides[MAX_POINTS_ON_WINDING+4];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ Vector mid = vec3_origin;
+ winding_t *f;
+ int maxpts;
+
+ in = *inout;
+ counts[0] = counts[1] = counts[2] = 0;
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->p[i], normal);
+ dot -= dist;
+ dists[i] = dot;
+ if (dot > epsilon)
+ {
+ sides[i] = SIDE_FRONT;
+ }
+ else if (dot < -epsilon)
+ {
+ sides[i] = SIDE_BACK;
+ }
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (!counts[0])
+ {
+ FreeWinding (in);
+ *inout = NULL;
+ return;
+ }
+ if (!counts[1])
+ return; // inout stays the same
+
+ maxpts = in->numpoints+4; // cant use counts[0]+2 because
+ // of fp grouping errors
+
+ f = AllocWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ Vector& p1 = in->p[i];
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ // generate a split point
+ Vector& p2 = in->p[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (normal[j] == 1)
+ mid[j] = dist;
+ else if (normal[j] == -1)
+ mid[j] = -dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, f->p[f->numpoints]);
+ f->numpoints++;
+ }
+
+ if (f->numpoints > maxpts)
+ Error ("ClipWinding: points exceeded estimate");
+ if (f->numpoints > MAX_POINTS_ON_WINDING)
+ Error ("ClipWinding: MAX_POINTS_ON_WINDING");
+
+ FreeWinding (in);
+ *inout = f;
+}
+
+
+/*
+=================
+ChopWinding
+
+Returns the fragment of in that is on the front side
+of the cliping plane. The original is freed.
+=================
+*/
+winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist)
+{
+ winding_t *f, *b;
+
+ ClipWindingEpsilon (in, normal, dist, ON_EPSILON, &f, &b);
+ FreeWinding (in);
+ if (b)
+ FreeWinding (b);
+ return f;
+}
+
+
+/*
+=================
+CheckWinding
+
+=================
+*/
+void CheckWinding (winding_t *w)
+{
+ int i, j;
+ vec_t d, edgedist;
+ Vector dir, edgenormal, facenormal;
+ vec_t area;
+ vec_t facedist;
+
+ if (w->numpoints < 3)
+ Error ("CheckWinding: %i points",w->numpoints);
+
+ area = WindingArea(w);
+ if (area < 1)
+ Error ("CheckWinding: %f area", area);
+
+ WindingPlane (w, facenormal, &facedist);
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ Vector& p1 = w->p[i];
+
+ for (j=0 ; j<3 ; j++)
+ {
+ if (p1[j] > MAX_COORD_INTEGER || p1[j] < MIN_COORD_INTEGER)
+ Error ("CheckFace: out of range: %f",p1[j]);
+ }
+
+ j = i+1 == w->numpoints ? 0 : i+1;
+
+ // check the point is on the face plane
+ d = DotProduct (p1, facenormal) - facedist;
+ if (d < -ON_EPSILON || d > ON_EPSILON)
+ Error ("CheckWinding: point off plane");
+
+ // check the edge isnt degenerate
+ Vector& p2 = w->p[j];
+ VectorSubtract (p2, p1, dir);
+
+ if (VectorLength (dir) < ON_EPSILON)
+ Error ("CheckWinding: degenerate edge");
+
+ CrossProduct (facenormal, dir, edgenormal);
+ VectorNormalize (edgenormal);
+ edgedist = DotProduct (p1, edgenormal);
+ edgedist += ON_EPSILON;
+
+ // all other points must be on front side
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ if (j == i)
+ continue;
+ d = DotProduct (w->p[j], edgenormal);
+ if (d > edgedist)
+ Error ("CheckWinding: non-convex");
+ }
+ }
+}
+
+
+/*
+============
+WindingOnPlaneSide
+============
+*/
+int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist)
+{
+ qboolean front, back;
+ int i;
+ vec_t d;
+
+ front = false;
+ back = false;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ d = DotProduct (w->p[i], normal) - dist;
+ if (d < -ON_EPSILON)
+ {
+ if (front)
+ return SIDE_CROSS;
+ back = true;
+ continue;
+ }
+ if (d > ON_EPSILON)
+ {
+ if (back)
+ return SIDE_CROSS;
+ front = true;
+ continue;
+ }
+ }
+
+ if (back)
+ return SIDE_BACK;
+ if (front)
+ return SIDE_FRONT;
+ return SIDE_ON;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: 2d point inside of winding test (assumes the point resides in the
+// winding plane)
+//-----------------------------------------------------------------------------
+bool PointInWinding( const Vector &pt, winding_t *pWinding )
+{
+ if( !pWinding )
+ return false;
+
+#if 0
+ //
+ // NOTE: this will be a quicker way to calculate this, however I don't
+ // know the trick off hand (post dot product tests??)
+ // TODO: look in graphics gems!!!! (cab)
+ //
+
+ Vector edge1, edge2;
+ for( int ndxPt = 0; ndxPt < pWinding->numpoints; ndxPt++ )
+ {
+ edge1 = pWinding->p[ndxPt] - pt;
+ edge2 = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pt;
+
+ VectorNormalize( edge1 );
+ VectorNormalize( edge2 );
+
+ if( edge2.Dot( edge1 ) < 0.0f )
+ return false;
+ }
+
+ return true;
+
+#else
+ Vector edge, toPt, cross, testCross;
+
+ //
+ // get the first normal to test
+ //
+ toPt = pt - pWinding->p[0];
+ edge = pWinding->p[1] - pWinding->p[0];
+ testCross = edge.Cross( toPt );
+ VectorNormalize( testCross );
+
+ for( int ndxPt = 1; ndxPt < pWinding->numpoints; ndxPt++ )
+ {
+ toPt = pt - pWinding->p[ndxPt];
+ edge = pWinding->p[(ndxPt+1)%pWinding->numpoints] - pWinding->p[ndxPt];
+ cross = edge.Cross( toPt );
+ VectorNormalize( cross );
+
+ if( cross.Dot( testCross ) < 0.0f )
+ return false;
+ }
+
+ return true;
+#endif
+}
+
+void TranslateWinding( winding_t *pWinding, const Vector &offset )
+{
+ for ( int i = 0; i < pWinding->numpoints; i++ )
+ {
+ pWinding->p[i] += offset;
+ }
+}
diff --git a/mp/src/utils/common/polylib.h b/mp/src/utils/common/polylib.h new file mode 100644 index 00000000..1750a82a --- /dev/null +++ b/mp/src/utils/common/polylib.h @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef POLYLIB_H
+#define POLYLIB_H
+#pragma once
+
+#ifndef MATHLIB_H
+#include "mathlib/mathlib.h"
+#endif
+
+struct winding_t
+{
+ int numpoints;
+ Vector *p; // variable sized
+ int maxpoints;
+ winding_t *next;
+};
+
+#define MAX_POINTS_ON_WINDING 64
+
+// you can define on_epsilon in the makefile as tighter
+// point on plane side epsilon
+// todo: need a world-space epsilon, a lightmap-space epsilon, and a texture space epsilon
+// or at least convert from a world-space epsilon to lightmap and texture space epsilons
+#ifndef ON_EPSILON
+#define ON_EPSILON 0.1
+#endif
+
+
+winding_t *AllocWinding (int points);
+vec_t WindingArea (winding_t *w);
+void WindingCenter (winding_t *w, Vector ¢er);
+vec_t WindingAreaAndBalancePoint( winding_t *w, Vector ¢er );
+void ClipWindingEpsilon (winding_t *in, const Vector &normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back);
+
+// translates everything by offset, then does the clip, then translates back (to keep precision)
+void ClipWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist, vec_t epsilon, winding_t **front, winding_t **back, const Vector &offset );
+
+void ClassifyWindingEpsilon( winding_t *in, const Vector &normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back, winding_t **on);
+void ClassifyWindingEpsilon_Offset( winding_t *in, const Vector &normal, vec_t dist,
+ vec_t epsilon, winding_t **front, winding_t **back, winding_t **on, const Vector &offset);
+
+winding_t *ChopWinding (winding_t *in, const Vector &normal, vec_t dist);
+winding_t *CopyWinding (winding_t *w);
+winding_t *ReverseWinding (winding_t *w);
+winding_t *BaseWindingForPlane (const Vector &normal, vec_t dist);
+void CheckWinding (winding_t *w);
+void WindingPlane (winding_t *w, Vector &normal, vec_t *dist);
+void RemoveColinearPoints (winding_t *w);
+int WindingOnPlaneSide (winding_t *w, const Vector &normal, vec_t dist);
+void FreeWinding (winding_t *w);
+void WindingBounds (winding_t *w, Vector &mins, Vector &maxs);
+
+void ChopWindingInPlace (winding_t **w, const Vector &normal, vec_t dist, vec_t epsilon);
+// frees the original if clipped
+
+bool PointInWinding( Vector const &pt, winding_t *pWinding );
+
+// translates a winding by offset
+void TranslateWinding( winding_t *pWinding, const Vector &offset );
+
+void pw(winding_t *w);
+
+
+#endif // POLYLIB_H
diff --git a/mp/src/utils/common/qfiles.h b/mp/src/utils/common/qfiles.h new file mode 100644 index 00000000..b1d2232f --- /dev/null +++ b/mp/src/utils/common/qfiles.h @@ -0,0 +1,42 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef QFILES_H
+#define QFILES_H
+#pragma once
+
+
+//
+// qfiles.h: quake file formats
+// This file must be identical in the quake and utils directories
+//
+
+#include "basetypes.h"
+#include "commonmacros.h"
+#include "worldsize.h"
+#include "bspfile.h"
+
+#define MAX_OSPATH 260
+#define MAX_QPATH 64
+
+/*
+========================================================================
+
+The .pak files are just a linear collapse of a directory tree
+
+========================================================================
+*/
+
+#define IDPAKHEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P')
+
+#endif // QFILES_H
diff --git a/mp/src/utils/common/scratchpad_helpers.cpp b/mp/src/utils/common/scratchpad_helpers.cpp new file mode 100644 index 00000000..9a3c2d74 --- /dev/null +++ b/mp/src/utils/common/scratchpad_helpers.cpp @@ -0,0 +1,103 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "scratchpad_helpers.h"
+#include "bspfile.h"
+#include "bsplib.h"
+
+
+void ScratchPad_DrawWinding(
+ IScratchPad3D *pPad,
+ int nPoints,
+ Vector *pPoints,
+ Vector vColor,
+ Vector vOffset )
+{
+ for ( int i=0; i < nPoints; i++ )
+ {
+ pPad->DrawLine( CSPVert( pPoints[i]+vOffset, vColor ), CSPVert( pPoints[(i+1)%nPoints]+vOffset, vColor ) );
+ }
+}
+
+
+void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber, const CSPColor &faceColor, const Vector &vOffset )
+{
+ // Draw the face's outline, then put text for its face index on it too.
+ CUtlVector<Vector> points;
+ for ( int iEdge = 0; iEdge < f->numedges; iEdge++ )
+ {
+ int v;
+ int se = dsurfedges[f->firstedge + iEdge];
+ if ( se < 0 )
+ v = dedges[-se].v[1];
+ else
+ v = dedges[se].v[0];
+
+ dvertex_t *dv = &dvertexes[v];
+ points.AddToTail( dv->point );
+ }
+
+ // Draw the outline.
+ Vector vCenter( 0, 0, 0 );
+ for ( int iEdge=0; iEdge < points.Count(); iEdge++ )
+ {
+ pPad->DrawLine( CSPVert( points[iEdge]+vOffset, faceColor ), CSPVert( points[(iEdge+1)%points.Count()]+vOffset, faceColor ) );
+ vCenter += points[iEdge];
+ }
+ vCenter /= points.Count();
+ vCenter += vOffset;
+
+ // Draw the text.
+ if ( iFaceNumber != -1 )
+ {
+ char str[64];
+ Q_snprintf( str, sizeof( str ), "%d", iFaceNumber );
+
+ CTextParams params;
+
+ params.m_bCentered = true;
+ params.m_bOutline = true;
+ params.m_flLetterWidth = 2;
+ params.m_vColor.Init( 1, 0, 0 );
+
+ VectorAngles( dplanes[f->planenum].normal, params.m_vAngles );
+ params.m_bTwoSided = true;
+
+ params.m_vPos = vCenter;
+
+ pPad->DrawText( str, params );
+ }
+}
+
+
+void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor )
+{
+ bool bAutoFlush = pPad->GetAutoFlush();
+ pPad->SetAutoFlush( false );
+
+ for ( int i=0; i < numleafs; i++ )
+ {
+ dleaf_t *l = &dleafs[i];
+ if ( l->contents & CONTENTS_DETAIL )
+ continue;
+
+ for ( int z=0; z < l->numleaffaces; z++ )
+ {
+ int iFace = dleaffaces[l->firstleafface+z];
+ dface_t *f = &dfaces[iFace];
+ ScratchPad_DrawFace( pPad, f, bDrawFaceNumbers ? i : -1 );
+ }
+ }
+
+ pPad->SetAutoFlush( bAutoFlush );
+}
+
+
+void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor )
+{
+ IScratchPad3D *pPad = ScratchPad3D_Create();
+ ScratchPad_DrawWorld( pPad, bDrawFaceNumbers );
+}
diff --git a/mp/src/utils/common/scratchpad_helpers.h b/mp/src/utils/common/scratchpad_helpers.h new file mode 100644 index 00000000..8f409fca --- /dev/null +++ b/mp/src/utils/common/scratchpad_helpers.h @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef SCRATCHPAD_HELPERS_H
+#define SCRATCHPAD_HELPERS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "iscratchpad3d.h"
+#include "bspfile.h"
+
+
+void ScratchPad_DrawWinding( IScratchPad3D *pPad, int nPoints, Vector *pPoints, Vector vColor, Vector vOffset = Vector(0,0,0) );
+
+void ScratchPad_DrawFace( IScratchPad3D *pPad, dface_t *f, int iFaceNumber = -1, const CSPColor &faceColor=CSPColor(1,1,1,1), const Vector &vOffset=Vector(0,0,0) );
+void ScratchPad_DrawWorld( IScratchPad3D *pPad, bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) );
+void ScratchPad_DrawWorld( bool bDrawFaceNumbers, const CSPColor &faceColor=CSPColor(1,1,1,1) );
+
+
+#endif // SCRATCHPAD_HELPERS_H
diff --git a/mp/src/utils/common/scriplib.cpp b/mp/src/utils/common/scriplib.cpp new file mode 100644 index 00000000..469c7885 --- /dev/null +++ b/mp/src/utils/common/scriplib.cpp @@ -0,0 +1,1349 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+
+// scriplib.c
+
+#include "tier1/strtools.h"
+#include "tier2/tier2.h"
+#include "cmdlib.h"
+#include "scriplib.h"
+#if defined( _X360 )
+#include "xbox\xbox_win32stubs.h"
+#endif
+#if defined(POSIX)
+#include "../../filesystem/linux_support.h"
+#include <sys/stat.h>
+#endif
+/*
+=============================================================================
+
+ PARSING STUFF
+
+=============================================================================
+*/
+
+typedef struct
+{
+ char filename[1024];
+ char *buffer,*script_p,*end_p;
+ int line;
+
+ char macrobuffer[4096];
+ char *macroparam[64];
+ char *macrovalue[64];
+ int nummacroparams;
+
+} script_t;
+
+#define MAX_INCLUDES 64
+script_t scriptstack[MAX_INCLUDES];
+script_t *script = NULL;
+int scriptline;
+
+char token[MAXTOKEN];
+qboolean endofscript;
+qboolean tokenready; // only true if UnGetToken was just called
+
+typedef struct
+{
+ char *param;
+ char *value;
+} variable_t;
+
+CUtlVector<variable_t> g_definevariable;
+
+/*
+Callback stuff
+*/
+
+void DefaultScriptLoadedCallback( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber )
+{
+ NULL;
+}
+
+SCRIPT_LOADED_CALLBACK g_pfnCallback = DefaultScriptLoadedCallback;
+
+SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback )
+{
+ SCRIPT_LOADED_CALLBACK pfnCallback = g_pfnCallback;
+ g_pfnCallback = pfnNewScriptLoadedCallback;
+ return pfnCallback;
+}
+
+/*
+==============
+AddScriptToStack
+==============
+*/
+void AddScriptToStack (char *filename, ScriptPathMode_t pathMode = SCRIPT_USE_ABSOLUTE_PATH)
+{
+ int size;
+
+ script++;
+ if (script == &scriptstack[MAX_INCLUDES])
+ Error ("script file exceeded MAX_INCLUDES");
+
+ if ( pathMode == SCRIPT_USE_RELATIVE_PATH )
+ Q_strncpy( script->filename, filename, sizeof( script->filename ) );
+ else
+ Q_strncpy (script->filename, ExpandPath (filename), sizeof( script->filename ) );
+
+ size = LoadFile (script->filename, (void **)&script->buffer);
+
+ // printf ("entering %s\n", script->filename);
+ if ( g_pfnCallback )
+ {
+ if ( script == scriptstack + 1 )
+ g_pfnCallback( script->filename, NULL, 0 );
+ else
+ g_pfnCallback( script->filename, script[-1].filename, script[-1].line );
+ }
+
+ script->line = 1;
+
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + size;
+}
+
+
+/*
+==============
+LoadScriptFile
+==============
+*/
+void LoadScriptFile (char *filename, ScriptPathMode_t pathMode)
+{
+ script = scriptstack;
+ AddScriptToStack (filename, pathMode);
+
+ endofscript = false;
+ tokenready = false;
+}
+
+
+/*
+==============
+==============
+*/
+
+script_t *macrolist[256];
+int nummacros;
+
+void DefineMacro( char *macroname )
+{
+ script_t *pmacro = (script_t *)malloc( sizeof( script_t ) );
+
+ strcpy( pmacro->filename, macroname );
+ pmacro->line = script->line;
+ pmacro->nummacroparams = 0;
+
+ char *mp = pmacro->macrobuffer;
+ char *cp = script->script_p;
+
+ while (TokenAvailable( ))
+ {
+ GetToken( false );
+
+ if (token[0] == '\\' && token[1] == '\\')
+ {
+ break;
+ }
+ cp = script->script_p;
+
+ pmacro->macroparam[pmacro->nummacroparams++] = mp;
+
+ strcpy( mp, token );
+ mp += strlen( token ) + 1;
+
+ if (mp >= pmacro->macrobuffer + sizeof( pmacro->macrobuffer ))
+ Error("Macro buffer overflow\n");
+ }
+ // roll back script_p to previous valid location
+ script->script_p = cp;
+
+ // find end of macro def
+ while (*cp && *cp != '\n')
+ {
+ //Msg("%d ", *cp );
+ if (*cp == '\\' && *(cp+1) == '\\')
+ {
+ // skip till end of line
+ while (*cp && *cp != '\n')
+ {
+ *cp = ' '; // replace with spaces
+ cp++;
+ }
+
+ if (*cp)
+ {
+ cp++;
+ }
+ }
+ else
+ {
+ cp++;
+ }
+ }
+
+ int size = (cp - script->script_p);
+
+ pmacro->buffer = (char *)malloc( size + 1);
+ memcpy( pmacro->buffer, script->script_p, size );
+ pmacro->buffer[size] = '\0';
+ pmacro->end_p = &pmacro->buffer[size];
+
+ macrolist[nummacros++] = pmacro;
+
+ script->script_p = cp;
+}
+
+
+void DefineVariable( char *variablename )
+{
+ variable_t v;
+
+ v.param = strdup( variablename );
+
+ GetToken( false );
+
+ v.value = strdup( token );
+
+ g_definevariable.AddToTail( v );
+}
+
+
+
+/*
+==============
+==============
+*/
+bool AddMacroToStack( char *macroname )
+{
+ // lookup macro
+ if (macroname[0] != '$')
+ return false;
+
+ int i;
+ for (i = 0; i < nummacros; i++)
+ {
+ if (strcmpi( macrolist[i]->filename, ¯oname[1] ) == 0)
+ {
+ break;
+ }
+ }
+ if (i == nummacros)
+ return false;
+
+ script_t *pmacro = macrolist[i];
+
+ // get tokens
+ script_t *pnext = script + 1;
+
+ pnext++;
+ if (pnext == &scriptstack[MAX_INCLUDES])
+ Error ("script file exceeded MAX_INCLUDES");
+
+ // get tokens
+ char *cp = pnext->macrobuffer;
+
+ pnext->nummacroparams = pmacro->nummacroparams;
+
+ for (i = 0; i < pnext->nummacroparams; i++)
+ {
+ GetToken(false);
+
+ strcpy( cp, token );
+ pnext->macroparam[i] = pmacro->macroparam[i];
+ pnext->macrovalue[i] = cp;
+
+ cp += strlen( token ) + 1;
+
+ if (cp >= pnext->macrobuffer + sizeof( pnext->macrobuffer ))
+ Error("Macro buffer overflow\n");
+ }
+
+ script = pnext;
+ strcpy( script->filename, pmacro->filename );
+
+ int size = pmacro->end_p - pmacro->buffer;
+ script->buffer = (char *)malloc( size + 1 );
+ memcpy( script->buffer, pmacro->buffer, size );
+ pmacro->buffer[size] = '\0';
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + size;
+ script->line = pmacro->line;
+
+ return true;
+}
+
+
+
+bool ExpandMacroToken( char *&token_p )
+{
+ if ( script->nummacroparams && *script->script_p == '$' )
+ {
+ char *cp = script->script_p + 1;
+
+ while ( *cp > 32 && *cp != '$' )
+ {
+ cp++;
+ }
+
+ // found a word with $'s on either end?
+ if (*cp != '$')
+ return false;
+
+ // get token pointer
+ char *tp = script->script_p + 1;
+ int len = (cp - tp);
+ *(tp + len) = '\0';
+
+ // lookup macro parameter
+ int index = 0;
+ for (index = 0; index < script->nummacroparams; index++)
+ {
+ if (stricmp( script->macroparam[index], tp ) == 0)
+ break;
+ }
+ if (index >= script->nummacroparams)
+ {
+ Error("unknown macro token \"%s\" in %s\n", tp, script->filename );
+ }
+
+ // paste token into
+ len = strlen( script->macrovalue[index] );
+ strcpy( token_p, script->macrovalue[index] );
+ token_p += len;
+
+ script->script_p = cp + 1;
+
+ if (script->script_p >= script->end_p)
+ Error ("Macro expand overflow\n");
+
+ if (token_p >= &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+
+ return true;
+ }
+ return false;
+}
+
+
+
+/*
+==============
+==============
+*/
+// FIXME: this should create a new script context so the individual tokens in the variable can be parsed
+bool ExpandVariableToken( char *&token_p )
+{
+ if ( *script->script_p == '$' )
+ {
+ char *cp = script->script_p + 1;
+
+ while ( *cp > 32 && *cp != '$' )
+ {
+ cp++;
+ }
+
+ // found a word with $'s on either end?
+ if (*cp != '$')
+ return false;
+
+ // get token pointer
+ char *tp = script->script_p + 1;
+ int len = (cp - tp);
+ *(tp + len) = '\0';
+
+ // lookup macro parameter
+
+ int index;
+ for (index = 0; index < g_definevariable.Count(); index++)
+ {
+ if (Q_strnicmp( g_definevariable[index].param, tp, len ) == 0)
+ break;
+ }
+
+ if (index >= g_definevariable.Count() )
+ {
+ Error("unknown variable token \"%s\" in %s\n", tp, script->filename );
+ }
+
+ // paste token into
+ len = strlen( g_definevariable[index].value );
+ strcpy( token_p, g_definevariable[index].value );
+ token_p += len;
+
+ script->script_p = cp + 1;
+
+ if (script->script_p >= script->end_p)
+ Error ("Macro expand overflow\n");
+
+ if (token_p >= &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+
+ return true;
+ }
+ return false;
+}
+
+
+
+/*
+==============
+ParseFromMemory
+==============
+*/
+void ParseFromMemory (char *buffer, int size)
+{
+ script = scriptstack;
+ script++;
+ if (script == &scriptstack[MAX_INCLUDES])
+ Error ("script file exceeded MAX_INCLUDES");
+ strcpy (script->filename, "memory buffer" );
+
+ script->buffer = buffer;
+ script->line = 1;
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + size;
+
+ endofscript = false;
+ tokenready = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used instead of ParseFromMemory to temporarily add a memory buffer
+// to the script stack. ParseFromMemory just blows away the stack.
+//-----------------------------------------------------------------------------
+void PushMemoryScript( char *pszBuffer, const int nSize )
+{
+ if ( script == NULL )
+ {
+ script = scriptstack;
+ }
+ script++;
+ if ( script == &scriptstack[MAX_INCLUDES] )
+ {
+ Error ( "script file exceeded MAX_INCLUDES" );
+ }
+ strcpy (script->filename, "memory buffer" );
+
+ script->buffer = pszBuffer;
+ script->line = 1;
+ script->script_p = script->buffer;
+ script->end_p = script->buffer + nSize;
+
+ endofscript = false;
+ tokenready = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used after calling PushMemoryScript to clean up the memory buffer
+// added to the script stack. The normal end of script terminates
+// all parsing at the end of a memory buffer even if there are more scripts
+// remaining on the script stack
+//-----------------------------------------------------------------------------
+bool PopMemoryScript()
+{
+ if ( V_stricmp( script->filename, "memory buffer" ) )
+ return false;
+
+ if ( script == scriptstack )
+ {
+ endofscript = true;
+ return false;
+ }
+ script--;
+ scriptline = script->line;
+
+ endofscript = false;
+
+ return true;
+}
+
+
+/*
+==============
+UnGetToken
+
+Signals that the current token was not used, and should be reported
+for the next GetToken. Note that
+
+GetToken (true);
+UnGetToken ();
+GetToken (false);
+
+could cross a line boundary.
+==============
+*/
+void UnGetToken (void)
+{
+ tokenready = true;
+}
+
+
+qboolean EndOfScript (qboolean crossline)
+{
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+
+ if (!strcmp (script->filename, "memory buffer"))
+ {
+ endofscript = true;
+ return false;
+ }
+
+ free (script->buffer);
+ script->buffer = NULL;
+ if (script == scriptstack+1)
+ {
+ endofscript = true;
+ return false;
+ }
+ script--;
+ scriptline = script->line;
+ // printf ("returning to %s\n", script->filename);
+ return GetToken (crossline);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given an absolute path, do a find first find next on it and build
+// a list of files. Physical file system only
+//-----------------------------------------------------------------------------
+static void FindFileAbsoluteList( CUtlVector< CUtlString > &outAbsolutePathNames, const char *pszFindName )
+{
+ char szPath[MAX_PATH];
+ V_strncpy( szPath, pszFindName, sizeof( szPath ) );
+ V_StripFilename( szPath );
+
+ char szResult[MAX_PATH];
+ FileFindHandle_t hFile = FILESYSTEM_INVALID_FIND_HANDLE;
+
+ for ( const char *pszFoundFile = g_pFullFileSystem->FindFirst( pszFindName, &hFile ); pszFoundFile && hFile != FILESYSTEM_INVALID_FIND_HANDLE; pszFoundFile = g_pFullFileSystem->FindNext( hFile ) )
+ {
+ V_ComposeFileName( szPath, pszFoundFile, szResult, sizeof( szResult ) );
+ outAbsolutePathNames.AddToTail( szResult );
+ }
+
+ g_pFullFileSystem->FindClose( hFile );
+}
+
+
+//-----------------------------------------------------------------------------
+// Data for checking for single character tokens while parsing
+//-----------------------------------------------------------------------------
+bool g_bCheckSingleCharTokens = false;
+CUtlString g_sSingleCharTokens;
+
+
+//-----------------------------------------------------------------------------
+// Sets whether the scriplib parser will do a special check for single
+// character tokens. Returns previous state of whether single character
+// tokens will be checked.
+//-----------------------------------------------------------------------------
+bool SetCheckSingleCharTokens( bool bCheck )
+{
+ const bool bRetVal = g_bCheckSingleCharTokens;
+
+ g_bCheckSingleCharTokens = bCheck;
+
+ return bRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the list of single character tokens to check if SetCheckSingleCharTokens
+// is turned on.
+//-----------------------------------------------------------------------------
+CUtlString SetSingleCharTokenList( const char *pszSingleCharTokenList )
+{
+ const CUtlString sRetVal = g_sSingleCharTokens;
+
+ if ( pszSingleCharTokenList )
+ {
+ g_sSingleCharTokens = pszSingleCharTokenList;
+ }
+
+ return sRetVal;
+}
+
+
+/*
+==============
+GetToken
+==============
+*/
+qboolean GetToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (tokenready) // is a token allready waiting?
+ {
+ tokenready = false;
+ return true;
+ }
+
+ // printf("script_p %x (%x)\n", script->script_p, script->end_p ); fflush( stdout );
+
+ if (script->script_p >= script->end_p)
+ {
+ return EndOfScript (crossline);
+ }
+
+ tokenready = false;
+
+ // skip space, ctrl chars
+skipspace:
+ while (*script->script_p <= 32)
+ {
+ if (script->script_p >= script->end_p)
+ {
+ return EndOfScript (crossline);
+ }
+ if (*(script->script_p++) == '\n')
+ {
+ if (!crossline)
+ {
+ Error ("Line %i is incomplete\n",scriptline);
+ }
+ scriptline = ++script->line;
+ }
+ }
+
+ if (script->script_p >= script->end_p)
+ {
+ return EndOfScript (crossline);
+ }
+
+ // strip single line comments
+ if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field
+ (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script->script_p++ != '\n')
+ {
+ if (script->script_p >= script->end_p)
+ {
+ return EndOfScript (crossline);
+ }
+ }
+ scriptline = ++script->line;
+ goto skipspace;
+ }
+
+ // strip out matching /* */ comments
+ if (*script->script_p == '/' && *((script->script_p)+1) == '*')
+ {
+ script->script_p += 2;
+ while (*script->script_p != '*' || *((script->script_p)+1) != '/')
+ {
+ if (*script->script_p++ != '\n')
+ {
+ if (script->script_p >= script->end_p)
+ {
+ return EndOfScript (crossline);
+ }
+
+ scriptline = ++script->line;
+ }
+ }
+ script->script_p += 2;
+ goto skipspace;
+ }
+
+ // copy token to buffer
+ token_p = token;
+
+ if (*script->script_p == '"')
+ {
+ // quoted token
+ script->script_p++;
+ while (*script->script_p != '"')
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+ script->script_p++;
+ }
+ else if ( g_bCheckSingleCharTokens && !g_sSingleCharTokens.IsEmpty() && strchr( g_sSingleCharTokens.String(), *script->script_p ) != NULL )
+ {
+ *token_p++ = *script->script_p++;
+ }
+ else // regular token
+ while ( *script->script_p > 32 && *script->script_p != ';')
+ {
+ if ( !ExpandMacroToken( token_p ) )
+ {
+ if ( !ExpandVariableToken( token_p ) )
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+
+ }
+ }
+ }
+
+ // add null to end of token
+ *token_p = 0;
+
+ // check for other commands
+ if ( !stricmp( token, "$include" ) )
+ {
+ GetToken( false );
+
+ bool bFallbackToToken = true;
+
+ CUtlVector< CUtlString > expandedPathList;
+
+ if ( CmdLib_ExpandWithBasePaths( expandedPathList, token ) > 0 )
+ {
+ for ( int i = 0; i < expandedPathList.Count(); ++i )
+ {
+ CUtlVector< CUtlString > findFileList;
+ FindFileAbsoluteList( findFileList, expandedPathList[i].String() );
+
+ if ( findFileList.Count() > 0 )
+ {
+ bFallbackToToken = false;
+
+ // Only add the first set of glob matches from the first base path
+ for ( int j = 0; j < findFileList.Count(); ++j )
+ {
+ AddScriptToStack( const_cast< char * >( findFileList[j].String() ) );
+ }
+
+ break;
+ }
+ }
+ }
+
+ if ( bFallbackToToken )
+ {
+ AddScriptToStack( token );
+ }
+
+ return GetToken( crossline );
+ }
+ else if (!stricmp (token, "$definemacro"))
+ {
+ GetToken (false);
+ DefineMacro(token);
+ return GetToken (crossline);
+ }
+ else if (!stricmp (token, "$definevariable"))
+ {
+ GetToken (false);
+ DefineVariable(token);
+ return GetToken (crossline);
+ }
+ else if (AddMacroToStack( token ))
+ {
+ return GetToken (crossline);
+ }
+
+ return true;
+}
+
+
+/*
+==============
+GetExprToken - use C mathematical operator parsing rules to split tokens instead of whitespace
+==============
+*/
+qboolean GetExprToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (tokenready) // is a token allready waiting?
+ {
+ tokenready = false;
+ return true;
+ }
+
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+
+ tokenready = false;
+
+//
+// skip space
+//
+skipspace:
+ while (*script->script_p <= 32)
+ {
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ if (*script->script_p++ == '\n')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ scriptline = ++script->line;
+ }
+ }
+
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+
+ if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field
+ (*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script->script_p++ != '\n')
+ if (script->script_p >= script->end_p)
+ return EndOfScript (crossline);
+ goto skipspace;
+ }
+
+//
+// copy token
+//
+ token_p = token;
+
+ if (*script->script_p == '"')
+ {
+ // quoted token
+ script->script_p++;
+ while (*script->script_p != '"')
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+ script->script_p++;
+ }
+ else
+ {
+ if ( V_isalpha( *script->script_p ) || *script->script_p == '_' )
+ {
+ // regular token
+ while ( V_isalnum( *script->script_p ) || *script->script_p == '_' )
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+ }
+ else if ( V_isdigit( *script->script_p ) || *script->script_p == '.' )
+ {
+ // regular token
+ while ( V_isdigit( *script->script_p ) || *script->script_p == '.' )
+ {
+ *token_p++ = *script->script_p++;
+ if (script->script_p == script->end_p)
+ break;
+ if (token_p == &token[MAXTOKEN])
+ Error ("Token too large on line %i\n",scriptline);
+ }
+ }
+ else
+ {
+ // single char
+ *token_p++ = *script->script_p++;
+ }
+ }
+
+ *token_p = 0;
+
+ if (!stricmp (token, "$include"))
+ {
+ GetToken (false);
+ AddScriptToStack (token);
+ return GetToken (crossline);
+ }
+
+ return true;
+}
+
+
+/*
+==============
+TokenAvailable
+
+Returns true if there is another token on the line
+==============
+*/
+qboolean TokenAvailable (void)
+{
+ char *search_p;
+
+ if (tokenready) // is a token allready waiting?
+ {
+ return true;
+ }
+
+ search_p = script->script_p;
+
+ if (search_p >= script->end_p)
+ return false;
+
+ while ( *search_p <= 32)
+ {
+ if (*search_p == '\n')
+ return false;
+ search_p++;
+ if (search_p == script->end_p)
+ return false;
+
+ }
+
+ if (*search_p == ';' || *search_p == '#' || // semicolon and # is comment field
+ (*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field
+ return false;
+
+ return true;
+}
+
+qboolean GetTokenizerStatus( char **pFilename, int *pLine )
+{
+ // is this the default state?
+ if (!script)
+ return false;
+
+ if (script->script_p >= script->end_p)
+ return false;
+
+ if (pFilename)
+ {
+ *pFilename = script->filename;
+ }
+ if (pLine)
+ {
+ *pLine = script->line;
+ }
+ return true;
+}
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef WIN32
+#include <direct.h>
+#include <io.h>
+#include <sys/utime.h>
+#endif
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "tier1/utlbuffer.h"
+
+class CScriptLib : public IScriptLib
+{
+public:
+ virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false );
+ virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode );
+ virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList );
+ virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize );
+ virtual void DeleteTemporaryFiles( const char *pFileMask );
+ virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB );
+ virtual bool DoesFileExist( const char *pFilename );
+
+private:
+
+ int GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList );
+ void RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList );
+};
+
+static CScriptLib g_ScriptLib;
+IScriptLib *scriptlib = &g_ScriptLib;
+IScriptLib *g_pScriptLib = &g_ScriptLib;
+
+//-----------------------------------------------------------------------------
+// Existence check
+//-----------------------------------------------------------------------------
+bool CScriptLib::DoesFileExist( const char *pFilename )
+{
+ return g_pFullFileSystem->FileExists( pFilename );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper utility, read file into buffer
+//-----------------------------------------------------------------------------
+bool CScriptLib::ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning )
+{
+ bool bSuccess = true;
+
+ if ( !g_pFullFileSystem->ReadFile( pSourceName, NULL, buffer ) )
+ {
+ if ( !bNoOpenFailureWarning )
+ {
+ Msg( "ReadFileToBuffer(): Error opening %s: %s\n", pSourceName, strerror( errno ) );
+ }
+ return false;
+ }
+
+ if ( bText )
+ {
+ // force it into text mode
+ buffer.SetBufferType( true, true );
+ }
+ else
+ {
+ buffer.SetBufferType( false, false );
+ }
+
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper utility, Write buffer to file
+//-----------------------------------------------------------------------------
+bool CScriptLib::WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode )
+{
+ char* ptr;
+ char dirPath[MAX_PATH];
+
+ bool bSuccess = true;
+
+ // create path
+ // prime and skip to first seperator
+ strcpy( dirPath, pTargetName );
+ ptr = strchr( dirPath, '\\' );
+ while ( ptr )
+ {
+ ptr = strchr( ptr+1, '\\' );
+ if ( ptr )
+ {
+ *ptr = '\0';
+ _mkdir( dirPath );
+ *ptr = '\\';
+ }
+ }
+
+ bool bDoWrite = false;
+ if ( writeMode == WRITE_TO_DISK_ALWAYS )
+ {
+ bDoWrite = true;
+ }
+ else if ( writeMode == WRITE_TO_DISK_UPDATE )
+ {
+ if ( DoesFileExist( pTargetName ) )
+ {
+ bDoWrite = true;
+ }
+ }
+
+ if ( bDoWrite )
+ {
+ bSuccess = g_pFullFileSystem->WriteFile( pTargetName, NULL, buffer );
+ }
+
+ return bSuccess;
+}
+
+//-----------------------------------------------------------------------------
+// Returns -1, 0, or 1.
+//-----------------------------------------------------------------------------
+int CScriptLib::CompareFileTime( const char *pFilenameA, const char *pFilenameB )
+{
+ int timeA = g_pFullFileSystem->GetFileTime( (char *)pFilenameA );
+ int timeB = g_pFullFileSystem->GetFileTime( (char *)pFilenameB );
+
+ if ( timeA == -1)
+ {
+ // file a not exist
+ timeA = 0;
+ }
+ if ( timeB == -1 )
+ {
+ // file b not exist
+ timeB = 0;
+ }
+
+ if ( (unsigned int)timeA < (unsigned int)timeB )
+ {
+ return -1;
+ }
+ else if ( (unsigned int)timeA > (unsigned int)timeB )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Make a temporary filename
+//-----------------------------------------------------------------------------
+char *CScriptLib::MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize )
+{
+ char *pBuffer = _tempnam( pchModPath, "mgd_" );
+ if ( pBuffer[0] == '\\' )
+ {
+ pBuffer++;
+ }
+ if ( pBuffer[strlen( pBuffer )-1] == '.' )
+ {
+ pBuffer[strlen( pBuffer )-1] = '\0';
+ }
+ V_snprintf( pPath, pathSize, "%s.tmp", pBuffer );
+
+ free( pBuffer );
+
+ return pPath;
+}
+
+//-----------------------------------------------------------------------------
+// Delete temporary files
+//-----------------------------------------------------------------------------
+void CScriptLib::DeleteTemporaryFiles( const char *pFileMask )
+{
+#if !defined( _X360 )
+ const char *pEnv = getenv( "temp" );
+ if ( !pEnv )
+ {
+ pEnv = getenv( "tmp" );
+ }
+
+ if ( pEnv )
+ {
+ char tempPath[MAX_PATH];
+ strcpy( tempPath, pEnv );
+ V_AppendSlash( tempPath, sizeof( tempPath ) );
+ strcat( tempPath, pFileMask );
+
+ CUtlVector<fileList_t> fileList;
+ FindFiles( tempPath, false, fileList );
+ for ( int i=0; i<fileList.Count(); i++ )
+ {
+ _unlink( fileList[i].fileName.String() );
+ }
+ }
+#else
+ AssertOnce( !"CScriptLib::DeleteTemporaryFiles: Not avail on 360\n" );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get list of files from current path that match pattern
+//-----------------------------------------------------------------------------
+int CScriptLib::GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList )
+{
+ char sourcePath[MAX_PATH];
+ char fullPath[MAX_PATH];
+ bool bFindDirs;
+
+ fileList.Purge();
+
+ strcpy( sourcePath, pDirPath );
+ int len = (int)strlen( sourcePath );
+ if ( !len )
+ {
+ strcpy( sourcePath, ".\\" );
+ }
+ else if ( sourcePath[len-1] != '\\' )
+ {
+ sourcePath[len] = '\\';
+ sourcePath[len+1] = '\0';
+ }
+
+ strcpy( fullPath, sourcePath );
+ if ( pPattern[0] == '\\' && pPattern[1] == '\0' )
+ {
+ // find directories only
+ bFindDirs = true;
+ strcat( fullPath, "*" );
+ }
+ else
+ {
+ // find files, use provided pattern
+ bFindDirs = false;
+ strcat( fullPath, pPattern );
+ }
+
+#ifdef WIN32
+ struct _finddata_t findData;
+ intptr_t h = _findfirst( fullPath, &findData );
+ if ( h == -1 )
+ {
+ return 0;
+ }
+
+ do
+ {
+ // dos attribute complexities i.e. _A_NORMAL is 0
+ if ( bFindDirs )
+ {
+ // skip non dirs
+ if ( !( findData.attrib & _A_SUBDIR ) )
+ continue;
+ }
+ else
+ {
+ // skip dirs
+ if ( findData.attrib & _A_SUBDIR )
+ continue;
+ }
+
+ if ( !stricmp( findData.name, "." ) )
+ continue;
+
+ if ( !stricmp( findData.name, ".." ) )
+ continue;
+
+ char fileName[MAX_PATH];
+ strcpy( fileName, sourcePath );
+ strcat( fileName, findData.name );
+
+ int j = fileList.AddToTail();
+ fileList[j].fileName.Set( fileName );
+ fileList[j].timeWrite = findData.time_write;
+ }
+ while ( !_findnext( h, &findData ) );
+
+ _findclose( h );
+#elif defined(POSIX)
+ FIND_DATA findData;
+ Q_FixSlashes( fullPath );
+ void *h = FindFirstFile( fullPath, &findData );
+ if ( (int)h == -1 )
+ {
+ return 0;
+ }
+
+ do
+ {
+ // dos attribute complexities i.e. _A_NORMAL is 0
+ if ( bFindDirs )
+ {
+ // skip non dirs
+ if ( !( findData.dwFileAttributes & S_IFDIR ) )
+ continue;
+ }
+ else
+ {
+ // skip dirs
+ if ( findData.dwFileAttributes & S_IFDIR )
+ continue;
+ }
+
+ if ( !stricmp( findData.cFileName, "." ) )
+ continue;
+
+ if ( !stricmp( findData.cFileName, ".." ) )
+ continue;
+
+ char fileName[MAX_PATH];
+ strcpy( fileName, sourcePath );
+ strcat( fileName, findData.cFileName );
+
+ int j = fileList.AddToTail();
+ fileList[j].fileName.Set( fileName );
+ struct stat statbuf;
+ if ( stat( fileName, &statbuf ) )
+#ifdef OSX
+ fileList[j].timeWrite = statbuf.st_mtimespec.tv_sec;
+#else
+ fileList[j].timeWrite = statbuf.st_mtime;
+#endif
+ else
+ fileList[j].timeWrite = 0;
+ }
+ while ( !FindNextFile( h, &findData ) );
+
+ FindClose( h );
+
+#else
+#error
+#endif
+
+
+ return fileList.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recursively determine directory tree
+//-----------------------------------------------------------------------------
+void CScriptLib::RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList )
+{
+ // recurse from source directory, get directories only
+ CUtlVector< fileList_t > fileList;
+ int dirCount = GetFileList( pDirPath, "\\", fileList );
+ if ( !dirCount )
+ {
+ // add directory name to search tree
+ int j = dirList.AddToTail();
+ dirList[j].Set( pDirPath );
+ return;
+ }
+
+ for ( int i=0; i<dirCount; i++ )
+ {
+ // form new path name, recurse into
+ RecurseFileTree_r( fileList[i].fileName.String(), depth+1, dirList );
+ }
+
+ int j = dirList.AddToTail();
+ dirList[j].Set( pDirPath );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generate a list of file matching mask
+//-----------------------------------------------------------------------------
+int CScriptLib::FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList )
+{
+ char dirPath[MAX_PATH];
+ char pattern[MAX_PATH];
+ char extension[MAX_PATH];
+
+ // get path only
+ strcpy( dirPath, pFileMask );
+ V_StripFilename( dirPath );
+
+ // get pattern only
+ V_FileBase( pFileMask, pattern, sizeof( pattern ) );
+ V_ExtractFileExtension( pFileMask, extension, sizeof( extension ) );
+ if ( extension[0] )
+ {
+ strcat( pattern, "." );
+ strcat( pattern, extension );
+ }
+
+ if ( !bRecurse )
+ {
+ GetFileList( dirPath, pattern, fileList );
+ }
+ else
+ {
+ // recurse and get the tree
+ CUtlVector< fileList_t > tempList;
+ CUtlVector< CUtlString > dirList;
+ RecurseFileTree_r( dirPath, 0, dirList );
+ for ( int i=0; i<dirList.Count(); i++ )
+ {
+ // iterate each directory found
+ tempList.Purge();
+ tempList.EnsureCapacity( dirList.Count() );
+
+ GetFileList( dirList[i].String(), pattern, tempList );
+
+ int start = fileList.AddMultipleToTail( tempList.Count() );
+ for ( int j=0; j<tempList.Count(); j++ )
+ {
+ fileList[start+j] = tempList[j];
+ }
+ }
+ }
+
+ return fileList.Count();
+}
diff --git a/mp/src/utils/common/scriplib.h b/mp/src/utils/common/scriplib.h new file mode 100644 index 00000000..6b119525 --- /dev/null +++ b/mp/src/utils/common/scriplib.h @@ -0,0 +1,96 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SCRIPLIB_H
+#define SCRIPLIB_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+enum ScriptPathMode_t
+{
+ SCRIPT_USE_ABSOLUTE_PATH,
+ SCRIPT_USE_RELATIVE_PATH
+};
+
+
+// scriplib.h
+
+#define MAXTOKEN 1024
+
+extern char token[MAXTOKEN];
+extern char *scriptbuffer,*script_p,*scriptend_p;
+extern int grabbed;
+extern int scriptline;
+extern qboolean endofscript;
+
+
+// If pathMode is SCRIPT_USE_ABSOLUTE_PATH, then it uses ExpandPath() on the filename before
+// trying to open it. Otherwise, it passes the filename straight into the filesystem
+// (so you can leave it as a relative path).
+void LoadScriptFile (char *filename, ScriptPathMode_t pathMode=SCRIPT_USE_ABSOLUTE_PATH);
+void ParseFromMemory (char *buffer, int size);
+
+qboolean GetToken (qboolean crossline);
+qboolean GetExprToken (qboolean crossline);
+void UnGetToken (void);
+qboolean TokenAvailable (void);
+qboolean GetTokenizerStatus( char **pFilename, int *pLine );
+bool SetCheckSingleCharTokens( bool bCheck );
+
+// SCRIPT_LOADED_CALLBACK:
+// Is called after the contents of a file is loaded.
+// pFilenameLoaded is the path of a file that got loaded.
+// pIncludedFromFileName is the name of the parent file or NULL if loaded because of "LoadScriptFile" toplevel call.
+// nIncludeLineNumber is the number of the line in the parent file with $include statement or 0 in case of "LoadScriptFile"
+typedef void ( * SCRIPT_LOADED_CALLBACK )( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber );
+
+// SetScriptLoadedCallback:
+// Sets the new callback for script loading.
+// Returns the previous callback function.
+SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback );
+
+#include "tier1/utlstring.h"
+#include "tier1/utlvector.h"
+
+CUtlString SetSingleCharTokenList( const char *pszSingleCharTokenList );
+
+class CUtlBuffer;
+
+enum DiskWriteMode_t
+{
+ WRITE_TO_DISK_NEVER,
+ WRITE_TO_DISK_ALWAYS,
+ WRITE_TO_DISK_UPDATE, // file must exist
+};
+
+struct fileList_t
+{
+ CUtlString fileName;
+ time_t timeWrite;
+};
+
+class IScriptLib
+{
+public:
+ virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false ) = 0;
+ virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ) = 0;
+ virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList ) = 0;
+ virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ) = 0;
+ virtual void DeleteTemporaryFiles( const char *pFileMask ) = 0;
+ virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB ) = 0;
+ virtual bool DoesFileExist( const char *pFilename ) = 0;
+};
+
+extern IScriptLib *scriptlib;
+
+
+#endif // SCRIPLIB_H
diff --git a/mp/src/utils/common/threads.cpp b/mp/src/utils/common/threads.cpp new file mode 100644 index 00000000..344943c9 --- /dev/null +++ b/mp/src/utils/common/threads.cpp @@ -0,0 +1,257 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#define USED
+
+#include <windows.h>
+#include "cmdlib.h"
+#define NO_THREAD_NAMES
+#include "threads.h"
+#include "pacifier.h"
+
+#define MAX_THREADS 16
+
+
+class CRunThreadsData
+{
+public:
+ int m_iThread;
+ void *m_pUserData;
+ RunThreadsFn m_Fn;
+};
+
+CRunThreadsData g_RunThreadsData[MAX_THREADS];
+
+
+int dispatch;
+int workcount;
+qboolean pacifier;
+
+qboolean threaded;
+bool g_bLowPriorityThreads = false;
+
+HANDLE g_ThreadHandles[MAX_THREADS];
+
+
+
+/*
+=============
+GetThreadWork
+
+=============
+*/
+int GetThreadWork (void)
+{
+ int r;
+
+ ThreadLock ();
+
+ if (dispatch == workcount)
+ {
+ ThreadUnlock ();
+ return -1;
+ }
+
+ UpdatePacifier( (float)dispatch / workcount );
+
+ r = dispatch;
+ dispatch++;
+ ThreadUnlock ();
+
+ return r;
+}
+
+
+ThreadWorkerFn workfunction;
+
+void ThreadWorkerFunction( int iThread, void *pUserData )
+{
+ int work;
+
+ while (1)
+ {
+ work = GetThreadWork ();
+ if (work == -1)
+ break;
+
+ workfunction( iThread, work );
+ }
+}
+
+void RunThreadsOnIndividual (int workcnt, qboolean showpacifier, ThreadWorkerFn func)
+{
+ if (numthreads == -1)
+ ThreadSetDefault ();
+
+ workfunction = func;
+ RunThreadsOn (workcnt, showpacifier, ThreadWorkerFunction);
+}
+
+
+/*
+===================================================================
+
+WIN32
+
+===================================================================
+*/
+
+int numthreads = -1;
+CRITICAL_SECTION crit;
+static int enter;
+
+
+class CCritInit
+{
+public:
+ CCritInit()
+ {
+ InitializeCriticalSection (&crit);
+ }
+} g_CritInit;
+
+
+
+void SetLowPriority()
+{
+ SetPriorityClass( GetCurrentProcess(), IDLE_PRIORITY_CLASS );
+}
+
+
+void ThreadSetDefault (void)
+{
+ SYSTEM_INFO info;
+
+ if (numthreads == -1) // not set manually
+ {
+ GetSystemInfo (&info);
+ numthreads = info.dwNumberOfProcessors;
+ if (numthreads < 1 || numthreads > 32)
+ numthreads = 1;
+ }
+
+ Msg ("%i threads\n", numthreads);
+}
+
+
+void ThreadLock (void)
+{
+ if (!threaded)
+ return;
+ EnterCriticalSection (&crit);
+ if (enter)
+ Error ("Recursive ThreadLock\n");
+ enter = 1;
+}
+
+void ThreadUnlock (void)
+{
+ if (!threaded)
+ return;
+ if (!enter)
+ Error ("ThreadUnlock without lock\n");
+ enter = 0;
+ LeaveCriticalSection (&crit);
+}
+
+
+// This runs in the thread and dispatches a RunThreadsFn call.
+DWORD WINAPI InternalRunThreadsFn( LPVOID pParameter )
+{
+ CRunThreadsData *pData = (CRunThreadsData*)pParameter;
+ pData->m_Fn( pData->m_iThread, pData->m_pUserData );
+ return 0;
+}
+
+
+void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority )
+{
+ Assert( numthreads > 0 );
+ threaded = true;
+
+ if ( numthreads > MAX_TOOL_THREADS )
+ numthreads = MAX_TOOL_THREADS;
+
+ for ( int i=0; i < numthreads ;i++ )
+ {
+ g_RunThreadsData[i].m_iThread = i;
+ g_RunThreadsData[i].m_pUserData = pUserData;
+ g_RunThreadsData[i].m_Fn = fn;
+
+ DWORD dwDummy;
+ g_ThreadHandles[i] = CreateThread(
+ NULL, // LPSECURITY_ATTRIBUTES lpsa,
+ 0, // DWORD cbStack,
+ InternalRunThreadsFn, // LPTHREAD_START_ROUTINE lpStartAddr,
+ &g_RunThreadsData[i], // LPVOID lpvThreadParm,
+ 0, // DWORD fdwCreate,
+ &dwDummy );
+
+ if ( ePriority == k_eRunThreadsPriority_UseGlobalState )
+ {
+ if( g_bLowPriorityThreads )
+ SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_LOWEST );
+ }
+ else if ( ePriority == k_eRunThreadsPriority_Idle )
+ {
+ SetThreadPriority( g_ThreadHandles[i], THREAD_PRIORITY_IDLE );
+ }
+ }
+}
+
+
+void RunThreads_End()
+{
+ WaitForMultipleObjects( numthreads, g_ThreadHandles, TRUE, INFINITE );
+ for ( int i=0; i < numthreads; i++ )
+ CloseHandle( g_ThreadHandles[i] );
+
+ threaded = false;
+}
+
+
+/*
+=============
+RunThreadsOn
+=============
+*/
+void RunThreadsOn( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData )
+{
+ int start, end;
+
+ start = Plat_FloatTime();
+ dispatch = 0;
+ workcount = workcnt;
+ StartPacifier("");
+ pacifier = showpacifier;
+
+#ifdef _PROFILE
+ threaded = false;
+ (*func)( 0 );
+ return;
+#endif
+
+
+ RunThreads_Start( fn, pUserData );
+ RunThreads_End();
+
+
+ end = Plat_FloatTime();
+ if (pacifier)
+ {
+ EndPacifier(false);
+ printf (" (%i)\n", end-start);
+ }
+}
+
+
diff --git a/mp/src/utils/common/threads.h b/mp/src/utils/common/threads.h new file mode 100644 index 00000000..e29e9aab --- /dev/null +++ b/mp/src/utils/common/threads.h @@ -0,0 +1,65 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef THREADS_H
+#define THREADS_H
+#pragma once
+
+
+// Arrays that are indexed by thread should always be MAX_TOOL_THREADS+1
+// large so THREADINDEX_MAIN can be used from the main thread.
+#define MAX_TOOL_THREADS 16
+#define THREADINDEX_MAIN (MAX_TOOL_THREADS)
+
+
+extern int numthreads;
+
+// If set to true, then all the threads that are created are low priority.
+extern bool g_bLowPriorityThreads;
+
+typedef void (*ThreadWorkerFn)( int iThread, int iWorkItem );
+typedef void (*RunThreadsFn)( int iThread, void *pUserData );
+
+
+enum ERunThreadsPriority
+{
+ k_eRunThreadsPriority_UseGlobalState=0, // Default.. uses g_bLowPriorityThreads to decide what to set the priority to.
+ k_eRunThreadsPriority_Normal, // Doesn't touch thread priorities.
+ k_eRunThreadsPriority_Idle // Sets threads to idle priority.
+};
+
+
+// Put the process into an idle priority class so it doesn't hog the UI.
+void SetLowPriority();
+
+void ThreadSetDefault (void);
+int GetThreadWork (void);
+
+void RunThreadsOnIndividual ( int workcnt, qboolean showpacifier, ThreadWorkerFn fn );
+
+void RunThreadsOn ( int workcnt, qboolean showpacifier, RunThreadsFn fn, void *pUserData=NULL );
+
+// This version doesn't track work items - it just runs your function and waits for it to finish.
+void RunThreads_Start( RunThreadsFn fn, void *pUserData, ERunThreadsPriority ePriority=k_eRunThreadsPriority_UseGlobalState );
+void RunThreads_End();
+
+void ThreadLock (void);
+void ThreadUnlock (void);
+
+
+#ifndef NO_THREAD_NAMES
+#define RunThreadsOn(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOn(n,p,f); }
+#define RunThreadsOnIndividual(n,p,f) { if (p) printf("%-20s ", #f ":"); RunThreadsOnIndividual(n,p,f); }
+#endif
+
+#endif // THREADS_H
diff --git a/mp/src/utils/common/tools_minidump.cpp b/mp/src/utils/common/tools_minidump.cpp new file mode 100644 index 00000000..a0c84209 --- /dev/null +++ b/mp/src/utils/common/tools_minidump.cpp @@ -0,0 +1,61 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <windows.h>
+#include <dbghelp.h>
+#include "tier0/minidump.h"
+#include "tools_minidump.h"
+
+static bool g_bToolsWriteFullMinidumps = false;
+static ToolsExceptionHandler g_pCustomExceptionHandler = NULL;
+
+
+// --------------------------------------------------------------------------------- //
+// Internal helpers.
+// --------------------------------------------------------------------------------- //
+
+static LONG __stdcall ToolsExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
+{
+ // Non VMPI workers write a minidump and show a crash dialog like normal.
+ int iType = MiniDumpNormal;
+ if ( g_bToolsWriteFullMinidumps )
+ iType = MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory;
+
+ WriteMiniDumpUsingExceptionInfo( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo, (MINIDUMP_TYPE)iType );
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+static LONG __stdcall ToolsExceptionFilter_Custom( struct _EXCEPTION_POINTERS *ExceptionInfo )
+{
+ // Run their custom handler.
+ g_pCustomExceptionHandler( ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo );
+ return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway)
+}
+
+
+// --------------------------------------------------------------------------------- //
+// Interface functions.
+// --------------------------------------------------------------------------------- //
+
+void EnableFullMinidumps( bool bFull )
+{
+ g_bToolsWriteFullMinidumps = bFull;
+}
+
+
+void SetupDefaultToolsMinidumpHandler()
+{
+ SetUnhandledExceptionFilter( ToolsExceptionFilter );
+}
+
+
+void SetupToolsMinidumpHandler( ToolsExceptionHandler fn )
+{
+ g_pCustomExceptionHandler = fn;
+ SetUnhandledExceptionFilter( ToolsExceptionFilter_Custom );
+}
diff --git a/mp/src/utils/common/tools_minidump.h b/mp/src/utils/common/tools_minidump.h new file mode 100644 index 00000000..dfb44a9b --- /dev/null +++ b/mp/src/utils/common/tools_minidump.h @@ -0,0 +1,35 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TOOLS_MINIDUMP_H
+#define TOOLS_MINIDUMP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+
+// Defaults to false. If true, it'll write larger minidump files with the contents
+// of global variables and following pointers from where the crash occurred.
+void EnableFullMinidumps( bool bFull );
+
+
+// This handler catches any crash, writes a minidump, and runs the default system
+// crash handler (which usually shows a dialog).
+void SetupDefaultToolsMinidumpHandler();
+
+
+// (Used by VMPI) - you specify your own crash handler.
+// Arguments passed to ToolsExceptionHandler
+// exceptionCode - exception code
+// pvExceptionInfo - on Win32 platform points to "struct _EXCEPTION_POINTERS"
+// otherwise NULL
+//
+typedef void (*ToolsExceptionHandler)( unsigned long exceptionCode, void *pvExceptionInfo );
+void SetupToolsMinidumpHandler( ToolsExceptionHandler fn );
+
+
+#endif // MINIDUMP_H
diff --git a/mp/src/utils/common/utilmatlib.cpp b/mp/src/utils/common/utilmatlib.cpp new file mode 100644 index 00000000..962bb3f5 --- /dev/null +++ b/mp/src/utils/common/utilmatlib.cpp @@ -0,0 +1,184 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+// C callable material system interface for the utils.
+
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include <cmdlib.h>
+#include "utilmatlib.h"
+#include "tier0/dbg.h"
+#include <windows.h>
+#include "filesystem.h"
+#include "materialsystem/materialsystem_config.h"
+#include "mathlib/Mathlib.h"
+
+void LoadMaterialSystemInterface( CreateInterfaceFn fileSystemFactory )
+{
+ if( g_pMaterialSystem )
+ return;
+
+ // materialsystem.dll should be in the path, it's in bin along with vbsp.
+ const char *pDllName = "materialsystem.dll";
+ CSysModule *materialSystemDLLHInst;
+ materialSystemDLLHInst = g_pFullFileSystem->LoadModule( pDllName );
+ if( !materialSystemDLLHInst )
+ {
+ Error( "Can't load MaterialSystem.dll\n" );
+ }
+
+ CreateInterfaceFn clientFactory = Sys_GetFactory( materialSystemDLLHInst );
+ if ( clientFactory )
+ {
+ g_pMaterialSystem = (IMaterialSystem *)clientFactory( MATERIAL_SYSTEM_INTERFACE_VERSION, NULL );
+ if ( !g_pMaterialSystem )
+ {
+ Error( "Could not get the material system interface from materialsystem.dll (" __FILE__ ")" );
+ }
+ }
+ else
+ {
+ Error( "Could not find factory interface in library MaterialSystem.dll" );
+ }
+
+ if (!g_pMaterialSystem->Init( "shaderapiempty.dll", 0, fileSystemFactory ))
+ {
+ Error( "Could not start the empty shader (shaderapiempty.dll)!" );
+ }
+}
+
+void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory )
+{
+ LoadMaterialSystemInterface( fileSystemFactory );
+ MaterialSystem_Config_t config;
+ g_pMaterialSystem->OverrideConfig( config, false );
+}
+
+void ShutdownMaterialSystem( )
+{
+ if ( g_pMaterialSystem )
+ {
+ g_pMaterialSystem->Shutdown();
+ g_pMaterialSystem = NULL;
+ }
+}
+
+MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain )
+{
+ IMaterial *pMat = g_pMaterialSystem->FindMaterial( materialName, TEXTURE_GROUP_OTHER, bComplain );
+ MaterialSystemMaterial_t matHandle = pMat;
+
+ if ( pFound )
+ {
+ *pFound = true;
+ if ( IsErrorMaterial( pMat ) )
+ *pFound = false;
+ }
+
+ return matHandle;
+}
+
+void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height )
+{
+ PreviewImageRetVal_t retVal;
+ ImageFormat dummyImageFormat;
+ IMaterial *material = ( IMaterial * )materialHandle;
+ bool translucent;
+ retVal = material->GetPreviewImageProperties( width, height, &dummyImageFormat, &translucent );
+ if (retVal != MATERIAL_PREVIEW_IMAGE_OK )
+ {
+#if 0
+ if (retVal == MATERIAL_PREVIEW_IMAGE_BAD )
+ {
+ Error( "problem getting preview image for %s",
+ g_pMaterialSystem->GetMaterialName( materialInfo[matID].materialHandle ) );
+ }
+#else
+ *width = 128;
+ *height = 128;
+#endif
+ }
+}
+
+void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect )
+{
+ IMaterial *material = ( IMaterial * )materialHandle;
+ const IMaterialVar *reflectivityVar;
+
+ bool found;
+ reflectivityVar = material->FindVar( "$reflectivity", &found, false );
+ if( !found )
+ {
+ Vector tmp;
+ material->GetReflectivity( tmp );
+ VectorCopy( tmp.Base(), reflectivityVect );
+ }
+ else
+ {
+ reflectivityVar->GetVecValue( reflectivityVect, 3 );
+ }
+}
+
+int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID )
+{
+ IMaterial *material = ( IMaterial * )materialHandle;
+ switch( propID )
+ {
+ case UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS:
+ return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
+
+ case UTILMATLIB_NEEDS_LIGHTMAP:
+ return material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP );
+
+ default:
+ Assert( 0 );
+ return 0;
+ }
+}
+
+int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID )
+{
+ IMaterial *material = ( IMaterial * )materialHandle;
+ switch( propID )
+ {
+ case UTILMATLIB_OPACITY:
+ if (material->IsTranslucent())
+ return UTILMATLIB_TRANSLUCENT;
+ if (material->IsAlphaTested())
+ return UTILMATLIB_ALPHATEST;
+ return UTILMATLIB_OPAQUE;
+
+ default:
+ Assert( 0 );
+ return 0;
+ }
+}
+
+const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName )
+{
+ IMaterial *material = ( IMaterial * )materialHandle;
+ IMaterialVar *var;
+ bool found;
+ var = material->FindVar( propertyName, &found, false );
+ if( found )
+ {
+ return var->GetStringValue();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle )
+{
+ IMaterial *material = ( IMaterial * )materialHandle;
+ return material->GetShaderName();
+}
diff --git a/mp/src/utils/common/utilmatlib.h b/mp/src/utils/common/utilmatlib.h new file mode 100644 index 00000000..f73a73d0 --- /dev/null +++ b/mp/src/utils/common/utilmatlib.h @@ -0,0 +1,41 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef UTILMATLIB_H
+#define UTILMATLIB_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define MATERIAL_NOT_FOUND NULL
+
+class IMaterialSystem;
+extern IMaterialSystem *g_pMaterialSystem;
+
+typedef void *MaterialSystemMaterial_t;
+
+#define UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS 0
+#define UTILMATLIB_NEEDS_LIGHTMAP 1
+#define UTILMATLIB_OPACITY 2
+
+enum { UTILMATLIB_ALPHATEST = 0, UTILMATLIB_OPAQUE, UTILMATLIB_TRANSLUCENT };
+
+void InitMaterialSystem( const char *materialBaseDirPath, CreateInterfaceFn fileSystemFactory );
+void ShutdownMaterialSystem( );
+MaterialSystemMaterial_t FindMaterial( const char *materialName, bool *pFound, bool bComplain = true );
+void GetMaterialDimensions( MaterialSystemMaterial_t materialHandle, int *width, int *height );
+int GetMaterialShaderPropertyBool( MaterialSystemMaterial_t materialHandle, int propID );
+int GetMaterialShaderPropertyInt( MaterialSystemMaterial_t materialHandle, int propID );
+const char *GetMaterialVar( MaterialSystemMaterial_t materialHandle, const char *propertyName );
+void GetMaterialReflectivity( MaterialSystemMaterial_t materialHandle, float *reflectivityVect );
+const char *GetMaterialShaderName( MaterialSystemMaterial_t materialHandle );
+
+
+#endif // UTILMATLIB_H
diff --git a/mp/src/utils/common/vmpi_tools_shared.cpp b/mp/src/utils/common/vmpi_tools_shared.cpp new file mode 100644 index 00000000..c753ce11 --- /dev/null +++ b/mp/src/utils/common/vmpi_tools_shared.cpp @@ -0,0 +1,374 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <windows.h>
+#include <dbghelp.h>
+#include "vmpi.h"
+#include "cmdlib.h"
+#include "vmpi_tools_shared.h"
+#include "tier1/strtools.h"
+#include "mpi_stats.h"
+#include "iphelpers.h"
+#include "tier0/minidump.h"
+
+
+// ----------------------------------------------------------------------------- //
+// Globals.
+// ----------------------------------------------------------------------------- //
+
+static bool g_bReceivedDirectoryInfo = false; // Have we gotten the qdir info yet?
+
+static bool g_bReceivedDBInfo = false;
+static CDBInfo g_DBInfo;
+static unsigned long g_JobPrimaryID;
+
+static int g_nDisconnects = 0; // Tracks how many remote processes have disconnected ungracefully.
+
+
+// ----------------------------------------------------------------------------- //
+// Shared dispatch code.
+// ----------------------------------------------------------------------------- //
+
+bool SharedDispatch( MessageBuffer *pBuf, int iSource, int iPacketID )
+{
+ char *pInPos = &pBuf->data[2];
+
+ switch ( pBuf->data[1] )
+ {
+ case VMPI_SUBPACKETID_DIRECTORIES:
+ {
+ Q_strncpy( gamedir, pInPos, sizeof( gamedir ) );
+ pInPos += strlen( pInPos ) + 1;
+
+ Q_strncpy( qdir, pInPos, sizeof( qdir ) );
+
+ g_bReceivedDirectoryInfo = true;
+ }
+ return true;
+
+ case VMPI_SUBPACKETID_DBINFO:
+ {
+ g_DBInfo = *((CDBInfo*)pInPos);
+ pInPos += sizeof( CDBInfo );
+ g_JobPrimaryID = *((unsigned long*)pInPos);
+
+ g_bReceivedDBInfo = true;
+ }
+ return true;
+
+ case VMPI_SUBPACKETID_CRASH:
+ {
+ char const chCrashInfoType = *pInPos;
+ pInPos += 2;
+ switch ( chCrashInfoType )
+ {
+ case 't':
+ Warning( "\nWorker '%s' dead: %s\n", VMPI_GetMachineName( iSource ), pInPos );
+ break;
+ case 'f':
+ {
+ int iFileSize = * reinterpret_cast< int const * >( pInPos );
+ pInPos += sizeof( iFileSize );
+
+ // Temp folder
+ char const *szFolder = NULL;
+ if ( !szFolder ) szFolder = getenv( "TEMP" );
+ if ( !szFolder ) szFolder = getenv( "TMP" );
+ if ( !szFolder ) szFolder = "c:";
+
+ // Base module name
+ char chModuleName[_MAX_PATH], *pModuleName = chModuleName;
+ ::GetModuleFileName( NULL, chModuleName, sizeof( chModuleName ) / sizeof( chModuleName[0] ) );
+
+ if ( char *pch = strrchr( chModuleName, '.' ) )
+ *pch = 0;
+ if ( char *pch = strrchr( chModuleName, '\\' ) )
+ *pch = 0, pModuleName = pch + 1;
+
+ // Current time
+ time_t currTime = ::time( NULL );
+ struct tm * pTime = ::localtime( &currTime );
+
+ // Number of minidumps this run
+ static int s_numMiniDumps = 0;
+ ++ s_numMiniDumps;
+
+ // Prepare the filename
+ char chSaveFileName[ 2 * _MAX_PATH ] = { 0 };
+ sprintf( chSaveFileName, "%s\\vmpi_%s_on_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp",
+ szFolder,
+ pModuleName,
+ VMPI_GetMachineName( iSource ),
+ pTime->tm_year + 1900, /* Year less 2000 */
+ pTime->tm_mon + 1, /* month (0 - 11 : 0 = January) */
+ pTime->tm_mday, /* day of month (1 - 31) */
+ pTime->tm_hour, /* hour (0 - 23) */
+ pTime->tm_min, /* minutes (0 - 59) */
+ pTime->tm_sec, /* seconds (0 - 59) */
+ s_numMiniDumps
+ );
+
+ if ( FILE *fDump = fopen( chSaveFileName, "wb" ) )
+ {
+ fwrite( pInPos, 1, iFileSize, fDump );
+ fclose( fDump );
+
+ Warning( "\nSaved worker crash minidump '%s', size %d byte(s).\n",
+ chSaveFileName, iFileSize );
+ }
+ else
+ {
+ Warning( "\nReceived worker crash minidump size %d byte(s), failed to save.\n", iFileSize );
+ }
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+CDispatchReg g_SharedDispatchReg( VMPI_SHARED_PACKET_ID, SharedDispatch );
+
+
+
+// ----------------------------------------------------------------------------- //
+// Module interfaces.
+// ----------------------------------------------------------------------------- //
+
+void SendQDirInfo()
+{
+ char cPacketID[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DIRECTORIES };
+
+ MessageBuffer mb;
+ mb.write( cPacketID, 2 );
+ mb.write( gamedir, strlen( gamedir ) + 1 );
+ mb.write( qdir, strlen( qdir ) + 1 );
+
+ VMPI_SendData( mb.data, mb.getLen(), VMPI_PERSISTENT );
+}
+
+
+void RecvQDirInfo()
+{
+ while ( !g_bReceivedDirectoryInfo )
+ VMPI_DispatchNextMessage();
+}
+
+
+void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID )
+{
+ char cPacketInfo[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DBINFO };
+ const void *pChunks[] = { cPacketInfo, pInfo, &jobPrimaryID };
+ int chunkLengths[] = { 2, sizeof( CDBInfo ), sizeof( jobPrimaryID ) };
+
+ VMPI_SendChunks( pChunks, chunkLengths, ARRAYSIZE( pChunks ), VMPI_PERSISTENT );
+}
+
+
+void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID )
+{
+ while ( !g_bReceivedDBInfo )
+ VMPI_DispatchNextMessage();
+
+ *pInfo = g_DBInfo;
+ *pJobPrimaryID = g_JobPrimaryID;
+}
+
+// If the file is successfully opened, read and sent returns the size of the file in bytes
+// otherwise returns 0 and nothing is sent
+int VMPI_SendFileChunk( const void *pvChunkPrefix, int lenPrefix, tchar const *ptchFileName )
+{
+ HANDLE hFile = NULL;
+ HANDLE hMapping = NULL;
+ void const *pvMappedData = NULL;
+ int iResult = 0;
+
+ hFile = ::CreateFile( ptchFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+ if ( !hFile || ( hFile == INVALID_HANDLE_VALUE ) )
+ goto done;
+
+ hMapping = ::CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
+ if ( !hMapping || ( hMapping == INVALID_HANDLE_VALUE ) )
+ goto done;
+
+ pvMappedData = ::MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
+ if ( !pvMappedData )
+ goto done;
+
+ int iMappedFileSize = ::GetFileSize( hFile, NULL );
+ if ( INVALID_FILE_SIZE == iMappedFileSize )
+ goto done;
+
+ // Send the data over VMPI
+ if ( VMPI_Send3Chunks(
+ pvChunkPrefix, lenPrefix,
+ &iMappedFileSize, sizeof( iMappedFileSize ),
+ pvMappedData, iMappedFileSize,
+ VMPI_MASTER_ID ) )
+ iResult = iMappedFileSize;
+
+ // Fall-through for cleanup code to execute
+done:
+ if ( pvMappedData )
+ ::UnmapViewOfFile( pvMappedData );
+
+ if ( hMapping && ( hMapping != INVALID_HANDLE_VALUE ) )
+ ::CloseHandle( hMapping );
+
+ if ( hFile && ( hFile != INVALID_HANDLE_VALUE ) )
+ ::CloseHandle( hFile );
+
+ return iResult;
+}
+
+void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert )
+{
+ static LONG crashHandlerCount = 0;
+ if ( InterlockedIncrement( &crashHandlerCount ) == 1 )
+ {
+ Msg( "\nFAILURE: '%s' (assert: %d)\n", pMessage, bAssert );
+
+ // Send a message to the master.
+ char crashMsg[4] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_CRASH, 't', ':' };
+
+ VMPI_Send2Chunks(
+ crashMsg,
+ sizeof( crashMsg ),
+ pMessage,
+ strlen( pMessage ) + 1,
+ VMPI_MASTER_ID );
+
+ // Now attempt to create a minidump with the given exception information
+ if ( pvExceptionInfo )
+ {
+ struct _EXCEPTION_POINTERS *pvExPointers = ( struct _EXCEPTION_POINTERS * ) pvExceptionInfo;
+ tchar tchMinidumpFileName[_MAX_PATH] = { 0 };
+ bool bSucceededWritingMinidump = WriteMiniDumpUsingExceptionInfo(
+ pvExPointers->ExceptionRecord->ExceptionCode,
+ pvExPointers,
+ ( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData ),
+ // ( MINIDUMP_TYPE )( MiniDumpWithDataSegs | MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithProcessThreadData | MiniDumpWithPrivateReadWriteMemory ),
+ // ( MINIDUMP_TYPE )( MiniDumpNormal ),
+ tchMinidumpFileName );
+ if ( bSucceededWritingMinidump )
+ {
+ crashMsg[2] = 'f';
+ VMPI_SendFileChunk( crashMsg, sizeof( crashMsg ), tchMinidumpFileName );
+ ::DeleteFile( tchMinidumpFileName );
+ }
+ }
+
+ // Let the messages go out.
+ Sleep( 500 );
+ }
+
+ InterlockedDecrement( &crashHandlerCount );
+}
+
+
+// This is called if we crash inside our crash handler. It just terminates the process immediately.
+LONG __stdcall VMPI_SecondExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
+{
+ TerminateProcess( GetCurrentProcess(), 2 );
+ return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway)
+}
+
+
+void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo )
+{
+ // This is called if we crash inside our crash handler. It just terminates the process immediately.
+ SetUnhandledExceptionFilter( VMPI_SecondExceptionFilter );
+
+ //DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
+
+ #define ERR_RECORD( name ) { name, #name }
+ struct
+ {
+ int code;
+ char *pReason;
+ } errors[] =
+ {
+ ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
+ ERR_RECORD( EXCEPTION_ARRAY_BOUNDS_EXCEEDED ),
+ ERR_RECORD( EXCEPTION_BREAKPOINT ),
+ ERR_RECORD( EXCEPTION_DATATYPE_MISALIGNMENT ),
+ ERR_RECORD( EXCEPTION_FLT_DENORMAL_OPERAND ),
+ ERR_RECORD( EXCEPTION_FLT_DIVIDE_BY_ZERO ),
+ ERR_RECORD( EXCEPTION_FLT_INEXACT_RESULT ),
+ ERR_RECORD( EXCEPTION_FLT_INVALID_OPERATION ),
+ ERR_RECORD( EXCEPTION_FLT_OVERFLOW ),
+ ERR_RECORD( EXCEPTION_FLT_STACK_CHECK ),
+ ERR_RECORD( EXCEPTION_FLT_UNDERFLOW ),
+ ERR_RECORD( EXCEPTION_ILLEGAL_INSTRUCTION ),
+ ERR_RECORD( EXCEPTION_IN_PAGE_ERROR ),
+ ERR_RECORD( EXCEPTION_INT_DIVIDE_BY_ZERO ),
+ ERR_RECORD( EXCEPTION_INT_OVERFLOW ),
+ ERR_RECORD( EXCEPTION_INVALID_DISPOSITION ),
+ ERR_RECORD( EXCEPTION_NONCONTINUABLE_EXCEPTION ),
+ ERR_RECORD( EXCEPTION_PRIV_INSTRUCTION ),
+ ERR_RECORD( EXCEPTION_SINGLE_STEP ),
+ ERR_RECORD( EXCEPTION_STACK_OVERFLOW ),
+ ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
+ };
+
+ int nErrors = sizeof( errors ) / sizeof( errors[0] );
+ int i=0;
+ char *pchReason = NULL;
+ char chUnknownBuffer[32];
+ for ( i; ( i < nErrors ) && !pchReason; i++ )
+ {
+ if ( errors[i].code == uCode )
+ pchReason = errors[i].pReason;
+ }
+
+ if ( i == nErrors )
+ {
+ sprintf( chUnknownBuffer, "Error code 0x%08X", uCode );
+ pchReason = chUnknownBuffer;
+ }
+
+ VMPI_HandleCrash( pchReason, pvExceptionInfo, true );
+
+ TerminateProcess( GetCurrentProcess(), 1 );
+}
+
+
+void HandleMPIDisconnect( int procID, const char *pReason )
+{
+ int nLiveWorkers = VMPI_GetCurrentNumberOfConnections() - g_nDisconnects - 1;
+
+ // We ran into the size limit before and it wasn't readily apparent that the size limit had
+ // been breached, so make sure to show errors about invalid packet sizes..
+ bool bOldSuppress = g_bSuppressPrintfOutput;
+ g_bSuppressPrintfOutput = ( Q_stristr( pReason, "invalid packet size" ) == 0 );
+
+ Warning( "\n\n--- WARNING: lost connection to '%s' (%s).\n", VMPI_GetMachineName( procID ), pReason );
+
+ if ( g_bMPIMaster )
+ {
+ Warning( "%d workers remain.\n\n", nLiveWorkers );
+
+ ++g_nDisconnects;
+ /*
+ if ( VMPI_GetCurrentNumberOfConnections() - g_nDisconnects <= 1 )
+ {
+ Error( "All machines disconnected!" );
+ }
+ */
+ }
+ else
+ {
+ VMPI_HandleAutoRestart();
+ Error( "Worker quitting." );
+ }
+
+ g_bSuppressPrintfOutput = bOldSuppress;
+}
+
+
diff --git a/mp/src/utils/common/vmpi_tools_shared.h b/mp/src/utils/common/vmpi_tools_shared.h new file mode 100644 index 00000000..7c22201f --- /dev/null +++ b/mp/src/utils/common/vmpi_tools_shared.h @@ -0,0 +1,45 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef VMPI_TOOLS_SHARED_H
+#define VMPI_TOOLS_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// Packet IDs.
+ #define VMPI_SUBPACKETID_DIRECTORIES 0 // qdir directories.
+ #define VMPI_SUBPACKETID_DBINFO 1 // MySQL database info.
+ #define VMPI_SUBPACKETID_CRASH 3 // A worker saying it crashed.
+ #define VMPI_SUBPACKETID_MULTICAST_ADDR 4 // Filesystem multicast address.
+
+
+class CDBInfo;
+class CIPAddr;
+
+
+// Send/receive the qdir info.
+void SendQDirInfo();
+void RecvQDirInfo();
+
+void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID );
+void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID );
+
+void SendMulticastIP( const CIPAddr *pAddr );
+void RecvMulticastIP( CIPAddr *pAddr );
+
+void VMPI_HandleCrash( const char *pMessage, void *pvExceptionInfo, bool bAssert );
+
+// Call this from an exception handler (set by SetUnhandledExceptionHandler).
+// uCode = ExceptionInfo->ExceptionRecord->ExceptionCode.
+// pvExceptionInfo = ExceptionInfo
+void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo );
+
+void HandleMPIDisconnect( int procID, const char *pReason );
+
+
+#endif // VMPI_TOOLS_SHARED_H
diff --git a/mp/src/utils/common/wadlib.c b/mp/src/utils/common/wadlib.c new file mode 100644 index 00000000..2b5bb6b1 --- /dev/null +++ b/mp/src/utils/common/wadlib.c @@ -0,0 +1,334 @@ +//========= Copyright � 1996-2005, Valve LLC, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// wad2lib.c
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+//#include <sys/file.h>
+#include <stdarg.h>
+
+#ifdef NeXT
+#include <libc.h>
+#endif
+#include "cmdlib.h"
+#include "wadlib.h"
+#include "commonmacros.h"
+
+/*
+============================================================================
+
+ WAD READING
+
+============================================================================
+*/
+
+
+lumpinfo_t *lumpinfo; // location of each lump on disk
+int numlumps;
+
+wadinfo_t header;
+FILE *wadhandle;
+
+
+/*
+====================
+W_OpenWad
+====================
+*/
+void W_OpenWad (char *filename)
+{
+ lumpinfo_t *lump_p;
+ unsigned i;
+ int length;
+
+//
+// open the file and add to directory
+//
+ wadhandle = SafeOpenRead (filename);
+ SafeRead (wadhandle, &header, sizeof(header));
+
+ if (!STRING_MATCHES_ID(header.identification,WAD_ID))
+ Error ("Wad file %s doesn't have %s identifier\n",filename, WAD_IDNAME);
+
+ header.numlumps = LittleLong(header.numlumps);
+ header.infotableofs = LittleLong(header.infotableofs);
+
+ numlumps = header.numlumps;
+
+ length = numlumps*sizeof(lumpinfo_t);
+ lumpinfo = malloc (length);
+ lump_p = lumpinfo;
+
+ fseek (wadhandle, header.infotableofs, SEEK_SET);
+ SafeRead (wadhandle, lumpinfo, length);
+
+//
+// Fill in lumpinfo
+//
+
+ for (i=0 ; i<numlumps ; i++,lump_p++)
+ {
+ lump_p->filepos = LittleLong(lump_p->filepos);
+ lump_p->size = LittleLong(lump_p->size);
+ }
+}
+
+
+
+void CleanupName (char *in, char *out)
+{
+ int i;
+
+ for (i=0 ; i<sizeof( ((lumpinfo_t *)0)->name ) ; i++ )
+ {
+ if (!in[i])
+ break;
+
+ out[i] = toupper(in[i]);
+ }
+
+ for ( ; i<sizeof( ((lumpinfo_t *)0)->name ); i++ )
+ out[i] = 0;
+}
+
+
+/*
+====================
+W_CheckNumForName
+
+Returns -1 if name not found
+====================
+*/
+int W_CheckNumForName (char *name)
+{
+ char cleanname[TEXTURE_NAME_LENGTH];
+ int v1,v2, v3, v4;
+ int i;
+ lumpinfo_t *lump_p;
+
+ CleanupName (name, cleanname);
+
+// make the name into four integers for easy compares
+
+ v1 = *(int *)cleanname;
+ v2 = *(int *)&cleanname[4];
+ v3 = *(int *)&cleanname[8];
+ v4 = *(int *)&cleanname[12];
+
+// find it
+
+ lump_p = lumpinfo;
+ for (i=0 ; i<numlumps ; i++, lump_p++)
+ {
+ if ( *(int *)lump_p->name == v1
+ && *(int *)&lump_p->name[4] == v2
+ && *(int *)&lump_p->name[8] == v3
+ && *(int *)&lump_p->name[12] == v4
+ && !strcmp( lump_p->name, cleanname ) )
+ return i;
+ }
+
+ return -1;
+}
+
+
+/*
+====================
+W_GetNumForName
+
+Calls W_CheckNumForName, but bombs out if not found
+====================
+*/
+int W_GetNumForName (char *name)
+{
+ int i;
+
+ i = W_CheckNumForName (name);
+ if (i != -1)
+ return i;
+
+ Error ("W_GetNumForName: %s not found!",name);
+ return -1;
+}
+
+
+/*
+====================
+W_LumpLength
+
+Returns the buffer size needed to load the given lump
+====================
+*/
+int W_LumpLength (int lump)
+{
+ if (lump >= numlumps)
+ Error ("W_LumpLength: %i >= numlumps",lump);
+ return lumpinfo[lump].size;
+}
+
+
+/*
+====================
+W_ReadLumpNum
+
+Loads the lump into the given buffer, which must be >= W_LumpLength()
+====================
+*/
+void W_ReadLumpNum (int lump, void *dest)
+{
+ lumpinfo_t *l;
+
+ if (lump >= numlumps)
+ Error ("W_ReadLump: %i >= numlumps",lump);
+ l = lumpinfo+lump;
+
+ fseek (wadhandle, l->filepos, SEEK_SET);
+ SafeRead (wadhandle, dest, l->size);
+}
+
+
+
+/*
+====================
+W_LoadLumpNum
+====================
+*/
+void *W_LoadLumpNum (int lump)
+{
+ void *buf;
+
+ if ((unsigned)lump >= numlumps)
+ Error ("W_CacheLumpNum: %i >= numlumps",lump);
+
+ buf = malloc (W_LumpLength (lump));
+ W_ReadLumpNum (lump, buf);
+
+ return buf;
+}
+
+
+/*
+====================
+W_LoadLumpName
+====================
+*/
+void *W_LoadLumpName (char *name)
+{
+ return W_LoadLumpNum (W_GetNumForName(name));
+}
+
+
+/*
+===============================================================================
+
+ WAD CREATION
+
+===============================================================================
+*/
+
+FILE *outwad;
+
+lumpinfo_t outinfo[4096];
+int outlumps;
+
+short (*wadshort) (short l);
+int (*wadlong) (int l);
+
+/*
+===============
+NewWad
+===============
+*/
+
+void NewWad (char *pathname, qboolean bigendien)
+{
+ outwad = SafeOpenWrite (pathname);
+ fseek (outwad, sizeof(wadinfo_t), SEEK_SET);
+ memset (outinfo, 0, sizeof(outinfo));
+
+ if (bigendien)
+ {
+ wadshort = BigShort;
+ wadlong = BigLong;
+ }
+ else
+ {
+ wadshort = LittleShort;
+ wadlong = LittleLong;
+ }
+
+ outlumps = 0;
+}
+
+
+/*
+===============
+AddLump
+===============
+*/
+
+void AddLump (char *name, void *buffer, int length, int type, int compress)
+{
+ lumpinfo_t *info;
+ int ofs;
+
+ info = &outinfo[outlumps];
+ outlumps++;
+
+ memset (info,0,sizeof(info));
+
+ strcpy (info->name, name);
+ Q_strupr (info->name);
+
+ ofs = ftell(outwad);
+ info->filepos = wadlong(ofs);
+ info->size = info->disksize = wadlong(length);
+ info->type = type;
+ info->compression = compress;
+
+// FIXME: do compression
+
+ SafeWrite (outwad, buffer, length);
+}
+
+
+/*
+===============
+WriteWad
+===============
+*/
+
+void WriteWad (int wad3)
+{
+ wadinfo_t header;
+ int ofs;
+
+// write the lumpingo
+ ofs = ftell(outwad);
+
+ SafeWrite (outwad, outinfo, outlumps*sizeof(lumpinfo_t) );
+
+// write the header
+
+// a program will be able to tell the ednieness of a wad by the id
+ ID_TO_STRING( WAD_ID, header.identification );
+
+ header.numlumps = wadlong(outlumps);
+ header.infotableofs = wadlong(ofs);
+
+ fseek (outwad, 0, SEEK_SET);
+ SafeWrite (outwad, &header, sizeof(header));
+ fclose (outwad);
+}
+
+
diff --git a/mp/src/utils/common/wadlib.h b/mp/src/utils/common/wadlib.h new file mode 100644 index 00000000..a8e4e09a --- /dev/null +++ b/mp/src/utils/common/wadlib.h @@ -0,0 +1,46 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+// wadlib.h
+
+//
+// wad reading
+//
+
+#define CMP_NONE 0
+#define CMP_LZSS 1
+
+#define TYP_NONE 0
+#define TYP_LABEL 1
+#define TYP_LUMPY 64 // 64 + grab command number
+
+#ifndef WADTYPES_H
+#include "wadtypes.h"
+#endif
+
+extern lumpinfo_t *lumpinfo; // location of each lump on disk
+extern int numlumps;
+extern wadinfo_t header;
+
+void W_OpenWad (char *filename);
+int W_CheckNumForName (char *name);
+int W_GetNumForName (char *name);
+int W_LumpLength (int lump);
+void W_ReadLumpNum (int lump, void *dest);
+void *W_LoadLumpNum (int lump);
+void *W_LoadLumpName (char *name);
+
+void CleanupName (char *in, char *out);
+
+//
+// wad creation
+//
+void NewWad (char *pathname, qboolean bigendien);
+void AddLump (char *name, void *buffer, int length, int type, int compress);
+void WriteWad (int wad3);
+
diff --git a/mp/src/utils/glview/glos.h b/mp/src/utils/glview/glos.h new file mode 100644 index 00000000..bc36bb1a --- /dev/null +++ b/mp/src/utils/glview/glos.h @@ -0,0 +1,21 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// GLOS.H
+//
+// This is an OS specific header file
+
+#include <windows.h>
+
+// disable data conversion warnings
+
+#pragma warning(disable : 4244) // MIPS
+#pragma warning(disable : 4136) // X86
+#pragma warning(disable : 4051) // ALPHA
+
+
+
diff --git a/mp/src/utils/glview/glview-2010.vcxproj b/mp/src/utils/glview/glview-2010.vcxproj new file mode 100644 index 00000000..32b29c92 --- /dev/null +++ b/mp/src/utils/glview/glview-2010.vcxproj @@ -0,0 +1,250 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Glview</ProjectName>
+ <ProjectGuid>{DC76828F-1DD4-7E83-371E-EA4058FEE050}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>glview</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>glview</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 glview.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\glview;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);glu32.lib;opengl32.lib;odbc32.lib;odbccp32.lib;winmm.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\glview.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/glview.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 glview.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\glview;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);glu32.lib;opengl32.lib;odbc32.lib;odbccp32.lib;winmm.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\glview.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/glview.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\common\cmdlib.h" />
+ <ClInclude Include="glos.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="glview.cpp" />
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\common\cmdlib.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="..\..\public\filesystem_init.cpp" />
+ <ClCompile Include="..\common\filesystem_tools.cpp" />
+ <ClCompile Include="..\common\physdll.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/glview/glview-2010.vcxproj.filters b/mp/src/utils/glview/glview-2010.vcxproj.filters new file mode 100644 index 00000000..4d5ea2a6 --- /dev/null +++ b/mp/src/utils/glview/glview-2010.vcxproj.filters @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\common files">
+ <UniqueIdentifier>{66CEFED7-D9FA-AC06-065F-BBA238DD0568}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\common\cmdlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="glos.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="glview.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\cmdlib.cpp">
+ <Filter>Source Files\common files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Source Files\common files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_init.cpp">
+ <Filter>Source Files\common files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\filesystem_tools.cpp">
+ <Filter>Source Files\common files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\physdll.cpp">
+ <Filter>Source Files\common files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/glview/glview.cpp b/mp/src/utils/glview/glview.cpp new file mode 100644 index 00000000..d60a6556 --- /dev/null +++ b/mp/src/utils/glview/glview.cpp @@ -0,0 +1,1427 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "glos.h"
+#include <gl/gl.h>
+#if _MSC_VER < 1600
+#include <gl/glaux.h>
+#endif
+#include <gl/glu.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "cmodel.h"
+#include "tier1/strtools.h"
+#include "physdll.h"
+#include "phyfile.h"
+#include "vphysics_interface.h"
+#include "tier0/icommandline.h"
+#include "tier0/vprof.h"
+
+HDC camdc;
+HGLRC baseRC;
+HWND camerawindow;
+HANDLE main_instance;
+
+/* YWB: 3/13/98
+ You run the program like normal with any file. If you want to read portals for the
+ file type, you type: glview -portal filename.gl0 (or whatever). glview will then
+ try to read in the .prt file filename.prt.
+
+ The portals are shown as white lines superimposed over your image. You can toggle the
+ view between showing portals or not by hitting the '2' key. The '1' key toggles
+ world polygons.
+
+ The 'b' key toggles blending modes.
+
+ If you don't want to depth buffer the portals, hit 'p'.
+
+ The command line parsing is inelegant but functional.
+
+ I sped up the KB movement and turn speed, too.
+ */
+
+// Vars added by YWB
+Vector g_Center; // Center of all read points, so camera is in a sensible place
+int g_nTotalPoints = 0; // Total points read, for calculating center
+int g_UseBlending = 0; // Toggle to use blending mode or not
+BOOL g_bReadPortals = 0; // Did we read in a portal file?
+BOOL g_bNoDepthPortals = 0; // Do we zbuffer the lines of the portals?
+int g_nPortalHighlight = -1; // The leaf we're viewing
+int g_nLeafHighlight = -1; // The leaf we're viewing
+BOOL g_bShowList1 = 1; // Show regular polygons?
+BOOL g_bShowList2 = 1; // Show portals?
+BOOL g_bShowLines = 0; // Show outlines of faces
+BOOL g_Active = TRUE;
+BOOL g_Update = TRUE;
+BOOL g_bDisp = FALSE;
+IPhysicsCollision *physcollision = NULL;
+// -----------
+static int g_Keys[255];
+void AppKeyDown( int key );
+void AppKeyUp( int key );
+
+
+BOOL ReadDisplacementFile( const char *filename );
+void DrawDisplacementData( void );
+
+#define BENCHMARK_PHY 0
+
+/*
+=================
+Error
+
+For abnormal program terminations
+=================
+*/
+void Error (char *error, ...)
+{
+ va_list argptr;
+ char text[1024];
+
+ va_start (argptr,error);
+ vsprintf (text, error,argptr);
+ va_end (argptr);
+
+ MessageBox(NULL, text, "Error", 0 /* MB_OK */ );
+
+ exit (1);
+}
+
+float origin[3] = {32, 32, 48};
+float angles[3];
+float forward[3], right[3], vup[3], vpn[3], vright[3];
+float width = 1024;
+float height = 768;
+
+float g_flMovementSpeed = 320.f; // Units / second (run speed of HL)
+#define SPEED_TURN 90 // Degrees / second
+
+#define VK_COMMA 188
+#define VK_PERIOD 190
+
+
+void KeyDown (int key)
+{
+ switch (key)
+ {
+ case VK_ESCAPE:
+ g_Active = FALSE;
+ break;
+
+ case VK_F1:
+ glEnable (GL_CULL_FACE);
+ glCullFace (GL_FRONT);
+ break;
+ case 'B':
+ g_UseBlending ^= 1;
+ if (g_UseBlending)
+ glEnable(GL_BLEND);// YWB TESTING
+ else
+ glDisable(GL_BLEND);
+ break;
+
+ case '1':
+ g_bShowList1 ^= 1;
+ break;
+ case '2':
+ g_bShowList2 ^= 1;
+ break;
+ case 'P':
+ g_bNoDepthPortals ^= 1;
+ break;
+ case 'L':
+ g_bShowLines ^= 1;
+ break;
+ }
+ g_Update = TRUE;
+}
+
+static BOOL g_Capture = FALSE;
+
+#define MOUSE_SENSITIVITY 0.2f
+#define MOUSE_SENSITIVITY_X (MOUSE_SENSITIVITY*1)
+#define MOUSE_SENSITIVITY_Y (MOUSE_SENSITIVITY*1)
+
+void Cam_MouseMoved( void )
+{
+ if ( g_Capture )
+ {
+ RECT rect;
+ int centerx, centery;
+ float deltax, deltay;
+ POINT cursorPoint;
+
+ GetWindowRect( camerawindow, &rect );
+
+ if ( rect.top < 0)
+ rect.top = 0;
+ if ( rect.left < 0)
+ rect.left = 0;
+
+ centerx = ( rect.left + rect.right ) / 2;
+ centery = ( rect.top + rect.bottom ) / 2;
+
+ GetCursorPos( &cursorPoint );
+ SetCursorPos( centerx, centery );
+
+ deltax = (cursorPoint.x - centerx) * MOUSE_SENSITIVITY_X;
+ deltay = (cursorPoint.y - centery) * MOUSE_SENSITIVITY_Y;
+
+ angles[1] -= deltax;
+ angles[0] -= deltay;
+
+ g_Update = TRUE;
+ }
+}
+
+int Test_Key( int key )
+{
+ int r = (g_Keys[ key ] != 0);
+
+ g_Keys[ key ] &= 0x01; // clear out debounce bit
+
+ if (r)
+ g_Update = TRUE;
+
+ return r;
+}
+
+// UNDONE: Probably should change the controls to match the game - but I don't know who relies on them
+// as of now.
+void Cam_Update( float frametime )
+{
+ if ( Test_Key( 'W' ) )
+ {
+ VectorMA (origin, g_flMovementSpeed*frametime, vpn, origin);
+ }
+ if ( Test_Key( 'S' ) )
+ {
+ VectorMA (origin, -g_flMovementSpeed*frametime, vpn, origin);
+ }
+ if ( Test_Key( 'A' ) )
+ {
+ VectorMA (origin, -g_flMovementSpeed*frametime, vright, origin);
+ }
+ if ( Test_Key( 'D' ) )
+ {
+ VectorMA (origin, g_flMovementSpeed*frametime, vright, origin);
+ }
+
+ if ( Test_Key( VK_UP ) )
+ {
+ VectorMA (origin, g_flMovementSpeed*frametime, forward, origin);
+ }
+ if ( Test_Key( VK_DOWN ) )
+ {
+ VectorMA (origin, -g_flMovementSpeed*frametime, forward, origin);
+ }
+
+ if ( Test_Key( VK_LEFT ) )
+ {
+ angles[1] += SPEED_TURN * frametime;
+ }
+ if ( Test_Key( VK_RIGHT ) )
+ {
+ angles[1] -= SPEED_TURN * frametime;
+ }
+ if ( Test_Key( 'F' ) )
+ {
+ origin[2] += g_flMovementSpeed*frametime;
+ }
+ if ( Test_Key( 'C' ) )
+ {
+ origin[2] -= g_flMovementSpeed*frametime;
+ }
+ if ( Test_Key( VK_INSERT ) )
+ {
+ angles[0] += SPEED_TURN * frametime;
+ if (angles[0] > 85)
+ angles[0] = 85;
+ }
+ if ( Test_Key( VK_DELETE ) )
+ {
+ angles[0] -= SPEED_TURN * frametime;
+ if (angles[0] < -85)
+ angles[0] = -85;
+ }
+ Cam_MouseMoved();
+}
+
+void Cam_BuildMatrix (void)
+{
+ float xa, ya;
+ float matrix[4][4];
+ int i;
+
+ xa = angles[0]/180*M_PI;
+ ya = angles[1]/180*M_PI;
+
+ // the movement matrix is kept 2d ?? do we want this?
+
+ forward[0] = cos(ya);
+ forward[1] = sin(ya);
+ right[0] = forward[1];
+ right[1] = -forward[0];
+
+ glGetFloatv (GL_PROJECTION_MATRIX, &matrix[0][0]);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ vright[i] = matrix[i][0];
+ vup[i] = matrix[i][1];
+ vpn[i] = matrix[i][2];
+ }
+
+ VectorNormalize (vright);
+ VectorNormalize (vup);
+ VectorNormalize (vpn);
+}
+
+void Draw (void)
+{
+ float screenaspect;
+ float yfov;
+
+ //glClearColor (0.5, 0.5, 0.5, 0);
+ glClearColor(0.0, 0.0, 0.0, 0); // Black Clearing YWB
+ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ //
+ // set up viewpoint
+ //
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity ();
+
+ screenaspect = (float)width/height;
+ yfov = 2*atan((float)height/width)*180/M_PI;
+ gluPerspective (yfov, screenaspect, 6, 20000);
+
+ glRotatef (-90, 1, 0, 0); // put Z going up
+ glRotatef (90, 0, 0, 1); // put Z going up
+ glRotatef (angles[0], 0, 1, 0);
+ glRotatef (-angles[1], 0, 0, 1);
+ glTranslatef (-origin[0], -origin[1], -origin[2]);
+
+ Cam_BuildMatrix ();
+
+ //
+ // set drawing parms
+ //
+ glShadeModel (GL_SMOOTH);
+
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glFrontFace(GL_CW); // YWB Carmack goes backward
+ glCullFace(GL_BACK); // Cull backfaces (qcsg used to spit out two sides, doesn't for -glview now)
+ glEnable(GL_CULL_FACE); // Enable face culling, just in case...
+ glDisable(GL_TEXTURE_2D);
+
+ // Blending function if enabled..
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ if (g_UseBlending)
+ {
+ glEnable(GL_BLEND);// YWB TESTING
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE); // Enable face culling, just in case...
+ }
+ else
+ {
+ glDisable(GL_BLEND);
+ glEnable(GL_DEPTH_TEST);
+ }
+ glDepthFunc (GL_LEQUAL);
+
+ if( g_bDisp )
+ {
+ DrawDisplacementData();
+ }
+ else
+ {
+ //
+ // draw the list
+ //
+ if (g_bShowList1)
+ glCallList (1);
+
+ if (g_bReadPortals)
+ {
+ if (g_bNoDepthPortals)
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE); // Disable face culling
+ if (g_bShowList2)
+ glCallList(2);
+ };
+
+ if (g_bShowLines)
+ glCallList(3);
+ }
+}
+
+void ReadPolyFileType(const char *name, int nList, BOOL drawLines)
+{
+ FILE *f;
+ int i, j, numverts;
+ float v[8];
+ int c;
+ int r;
+ float divisor;
+
+ f = fopen (name, "rt");
+ if (!f)
+ Error ("Couldn't open %s", name);
+
+ if (g_bReadPortals)
+ divisor = 2.0f;
+ else
+ divisor = 1.0f;
+
+ c = 0;
+ glNewList (nList, GL_COMPILE);
+
+ for (i = 0; i < 3; i++) // Find the center point so we can put the viewer there by default
+ g_Center[i] = 0.0f;
+
+ if (drawLines) // Slight hilite
+ glLineWidth(1.5);
+
+ while (1)
+ {
+ r = fscanf( f, "%i\n", &numverts);
+ if (!r || r == EOF)
+ break;
+
+ if ( c > 65534*8)
+ break;
+
+ if (drawLines || numverts == 2)
+ glBegin(GL_LINE_LOOP);
+ else
+ glBegin (GL_POLYGON);
+
+ for (i=0 ; i<numverts ; i++)
+ {
+ r = fscanf( f, "%f %f %f %f %f %f\n", &v[0], &v[1],
+ &v[2], &v[3], &v[4], &v[5]);
+
+ /*
+ if (!(fabs( v[0] ) < 32768.0&& fabs( v[1] ) < 32768.0 && fabs( v[2] ) < 32768.0 ) )
+ Error( "Out of range data\n");
+ */
+
+ /*
+ if (v[3] <= 0.1 && v[4] <= 0.1 && v[5] <= 0.1 )
+ continue;
+ */
+
+ if (drawLines) // YELLOW OUTLINES
+ glColor4f(1.0, 1.0, 0.0, 0.5);
+ else
+ {
+ if (g_bReadPortals) // Gray scale it, leave portals blue
+ {
+ if (fabs(fabs(v[5]) - 1.0f) < 0.01) // Is this a detail brush (color 0,0,1 blue)
+ {
+ glColor4f (v[3],v[4],v[5],0.5);
+ }
+ else // Normal brush, gray scale it...
+ {
+ v[3] += v[4] + v[5];
+ v[3]/= 3.0f;
+ glColor4f (v[3]/divisor, v[3]/divisor, v[3]/divisor, 0.6);
+ }
+ }
+ else
+ {
+ v[3] = pow( v[3], (float)(1.0 / 2.2) );
+ v[4] = pow( v[4], (float)(1.0 / 2.2) );
+ v[5] = pow( v[5], (float)(1.0 / 2.2) );
+
+ glColor4f (v[3]/divisor, v[4]/divisor, v[5]/divisor, 0.6); // divisor is one, bright colors
+ };
+ };
+ glVertex3f (v[0], v[1], v[2]);
+
+ for (j = 0; j < 3; j++)
+ {
+ g_Center[j] += v[j];
+ }
+
+ g_nTotalPoints++;
+ }
+ glEnd ();
+ c++;
+ }
+
+ if (f)
+ fclose(f);
+
+ glEndList ();
+
+ if (g_nTotalPoints > 0) // Avoid division by zero
+ {
+ for (i = 0; i < 3; i++)
+ {
+ g_Center[i] = g_Center[i]/(float)g_nTotalPoints; // Calculate center...
+ origin[i] = g_Center[i];
+ }
+ }
+}
+
+#if BENCHMARK_PHY
+#define NUM_COLLISION_TESTS 2500
+#include "gametrace.h"
+#include "fmtstr.h"
+
+
+struct testlist_t
+{
+ Vector start;
+ Vector end;
+ Vector normal;
+ bool hit;
+};
+
+const float baselineTotal = 120.16f;
+const float baselineRay = 28.25f;
+const float baselineBox = 91.91f;
+#define IMPROVEMENT_FACTOR(x,baseline) (baseline/(x))
+#define IMPROVEMENT_PERCENT(x,baseline) (((baseline-(x)) / baseline) * 100.0f)
+
+testlist_t g_Traces[NUM_COLLISION_TESTS];
+void Benchmark_PHY( const CPhysCollide *pCollide )
+{
+ int i;
+ Msg( "Testing collision system\n" );
+ Vector start = vec3_origin;
+ static Vector *targets = NULL;
+ static bool first = true;
+ static float test[2] = {1,1};
+ if ( first )
+ {
+ float radius = 0;
+ float theta = 0;
+ float phi = 0;
+ for ( int i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ radius += NUM_COLLISION_TESTS * 123.123f;
+ radius = fabs(fmod(radius, 128));
+ theta += NUM_COLLISION_TESTS * 0.76f;
+ theta = fabs(fmod(theta, DEG2RAD(360)));
+ phi += NUM_COLLISION_TESTS * 0.16666666f;
+ phi = fabs(fmod(phi, DEG2RAD(180)));
+
+ float st, ct, sp, cp;
+ SinCos( theta, &st, &ct );
+ SinCos( phi, &sp, &cp );
+ st = sin(theta);
+ ct = cos(theta);
+ sp = sin(phi);
+ cp = cos(phi);
+
+ g_Traces[i].start.x = radius * ct * sp;
+ g_Traces[i].start.y = radius * st * sp;
+ g_Traces[i].start.z = radius * cp;
+ }
+ first = false;
+ }
+
+ float duration = 0;
+ Vector size[2];
+ size[0].Init(0,0,0);
+ size[1].Init(16,16,16);
+ unsigned int dots = 0;
+
+#if VPROF_LEVEL > 0
+ g_VProfCurrentProfile.Reset();
+ g_VProfCurrentProfile.ResetPeaks();
+ g_VProfCurrentProfile.Start();
+#endif
+ unsigned int hitCount = 0;
+ double startTime = Plat_FloatTime();
+ trace_t tr;
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ physcollision->TraceBox( g_Traces[i].start, start, -size[0], size[0], pCollide, vec3_origin, vec3_angle, &tr );
+ if ( tr.DidHit() )
+ {
+ g_Traces[i].end = tr.endpos;
+ g_Traces[i].normal = tr.plane.normal;
+ g_Traces[i].hit = true;
+ hitCount++;
+ }
+ else
+ {
+ g_Traces[i].hit = false;
+ }
+ }
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ physcollision->TraceBox( g_Traces[i].start, start, -size[1], size[1], pCollide, vec3_origin, vec3_angle, &tr );
+ }
+ duration = Plat_FloatTime() - startTime;
+ {
+ unsigned int msSupp = physcollision->ReadStat( 100 );
+ unsigned int msGJK = physcollision->ReadStat( 101 );
+ unsigned int msMesh = physcollision->ReadStat( 102 );
+ CFmtStr str("%d ms total %d ms gjk %d mesh solve\n", msSupp, msGJK, msMesh );
+ OutputDebugStr( str.Access() );
+ }
+
+#if VPROF_LEVEL > 0
+ g_VProfCurrentProfile.MarkFrame();
+ g_VProfCurrentProfile.Stop();
+ g_VProfCurrentProfile.Reset();
+ g_VProfCurrentProfile.ResetPeaks();
+ g_VProfCurrentProfile.Start();
+#endif
+ hitCount = 0;
+ startTime = Plat_FloatTime();
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ physcollision->TraceBox( g_Traces[i].start, start, -size[0], size[0], pCollide, vec3_origin, vec3_angle, &tr );
+ if ( tr.DidHit() )
+ {
+ g_Traces[i].end = tr.endpos;
+ g_Traces[i].normal = tr.plane.normal;
+ g_Traces[i].hit = true;
+ hitCount++;
+ }
+ else
+ {
+ g_Traces[i].hit = false;
+ }
+#if VPROF_LEVEL > 0
+ g_VProfCurrentProfile.MarkFrame();
+#endif
+ }
+ double midTime = Plat_FloatTime();
+ for ( i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ physcollision->TraceBox( g_Traces[i].start, start, -size[1], size[1], pCollide, vec3_origin, vec3_angle, &tr );
+#if VPROF_LEVEL > 0
+ g_VProfCurrentProfile.MarkFrame();
+#endif
+ }
+ double endTime = Plat_FloatTime();
+ duration = endTime - startTime;
+ {
+ CFmtStr str("%d collisions in %.2f ms [%.2f X] %d hits\n", NUM_COLLISION_TESTS, duration*1000, IMPROVEMENT_FACTOR(duration*1000.0f, baselineTotal), hitCount );
+ OutputDebugStr( str.Access() );
+ }
+ {
+ float rayTime = (midTime - startTime) * 1000.0f;
+ float boxTime = (endTime - midTime)*1000.0f;
+ CFmtStr str("%.2f ms rays [%.2f X] %.2f ms boxes [%.2f X]\n", rayTime, IMPROVEMENT_FACTOR(rayTime, baselineRay), boxTime, IMPROVEMENT_FACTOR(boxTime, baselineBox));
+ OutputDebugStr( str.Access() );
+ }
+
+ {
+ unsigned int msSupp = physcollision->ReadStat( 100 );
+ unsigned int msGJK = physcollision->ReadStat( 101 );
+ unsigned int msMesh = physcollision->ReadStat( 102 );
+ CFmtStr str("%d ms total %d ms gjk %d mesh solve\n", msSupp, msGJK, msMesh );
+ OutputDebugStr( str.Access() );
+ }
+#if VPROF_LEVEL > 0
+ g_VProfCurrentProfile.Stop();
+ g_VProfCurrentProfile.OutputReport( VPRT_FULL & ~VPRT_HIERARCHY, NULL );
+#endif
+
+ // draw the traces in yellow
+ glColor3f( 1.0f, 1.0f, 0.0f );
+ glBegin( GL_LINES );
+ for ( int i = 0; i < NUM_COLLISION_TESTS; i++ )
+ {
+ if ( !g_Traces[i].hit )
+ continue;
+ glVertex3fv( g_Traces[i].end.Base() );
+ Vector tmp = g_Traces[i].end + g_Traces[i].normal * 10.0f;
+ glVertex3fv( tmp.Base() );
+ }
+ glEnd();
+}
+#endif
+
+struct phyviewparams_t
+{
+ Vector mins;
+ Vector maxs;
+ Vector offset;
+ QAngle angles;
+ int outputType;
+
+ void Defaults()
+ {
+ ClearBounds(mins, maxs);
+ offset.Init();
+ outputType = GL_POLYGON;
+ angles.Init();
+ }
+};
+
+
+void AddVCollideToList( phyheader_t &header, vcollide_t &collide, phyviewparams_t ¶ms )
+{
+ matrix3x4_t xform;
+ AngleMatrix( params.angles, params.offset, xform );
+ ClearBounds( params.mins, params.maxs );
+ for ( int i = 0; i < header.solidCount; i++ )
+ {
+ ICollisionQuery *pQuery = physcollision->CreateQueryModel( collide.solids[i] );
+ for ( int j = 0; j < pQuery->ConvexCount(); j++ )
+ {
+ for ( int k = 0; k < pQuery->TriangleCount(j); k++ )
+ {
+ Vector verts[3];
+ pQuery->GetTriangleVerts( j, k, verts );
+ Vector v0,v1,v2;
+ VectorTransform( verts[0], xform, v0 );
+ VectorTransform( verts[1], xform, v1 );
+ VectorTransform( verts[2], xform, v2 );
+ AddPointToBounds( v0, params.mins, params.maxs );
+ AddPointToBounds( v1, params.mins, params.maxs );
+ AddPointToBounds( v2, params.mins, params.maxs );
+
+ glBegin(params.outputType);
+ glColor3ub( 255, 0, 0 );
+ glVertex3fv( v0.Base() );
+ glColor3ub( 0, 255, 0 );
+ glVertex3fv( v1.Base() );
+ glColor3ub( 0, 0, 255 );
+ glVertex3fv( v2.Base() );
+ glEnd();
+ }
+ }
+ physcollision->DestroyQueryModel( pQuery );
+ }
+}
+
+void GL_DrawLine( const Vector &start, const Vector &dir, float length, int r, int g, int b )
+{
+ Vector end = start + (dir*length);
+ glBegin( GL_LINES );
+ glColor3ub(r,g,b);
+ glVertex3fv( start.Base() );
+ glVertex3fv( end.Base() );
+ glEnd();
+}
+
+void GL_DrawBox( Vector origin, float size, int r, int g, int b )
+{
+ Vector mins = origin - Vector(size,size,size);
+ Vector maxs = origin + Vector(size,size,size);
+ const float *v[2] = {mins.Base(), maxs.Base()};
+
+ Vector start, end;
+ {
+ for ( int i = 0; i < 3; i++ )
+ {
+ int a0 = i;
+ int a1 = (i+1)%3;
+ int a2 = (i+2)%3;
+ for ( int j = 0; j < 2; j++ )
+ {
+ for ( int k = 0; k < 2; k++ )
+ {
+ start[a0] = v[0][a0];
+ end[a0] = v[1][a0];
+ start[a1] = v[j][a1];
+ end[a1] = v[j][a1];
+ start[a2] = v[k][a2];
+ end[a2] = v[k][a2];
+ GL_DrawLine( start, end-start, 1, r, g, b );
+ }
+ }
+ }
+ }
+ for ( int axis = 0; axis < 3; axis++ )
+ {
+ int a0 = axis;
+ int a1 = (axis+1)%3;
+ int a2 = (axis+2)%3;
+ start[a0] = v[0][a0];
+ end[a0] = v[1][a0];
+ start[a1] = 0.5f *(v[0][a1]+v[1][a1]);
+ end[a1] = 0.5f *(v[0][a1]+v[1][a1]);
+ start[a2] = 0.5f *(v[0][a2]+v[1][a2]);
+ end[a2] = 0.5f *(v[0][a2]+v[1][a2]);
+ GL_DrawLine( start, end-start, 1, r, g, b );
+ }
+}
+
+
+void ReadPHYFile(const char *name, phyviewparams_t ¶ms )
+{
+ FILE *fp = fopen (name, "rb");
+ if (!fp)
+ Error ("Couldn't open %s", name);
+
+ phyheader_t header;
+
+ fread( &header, sizeof(header), 1, fp );
+ if ( header.size != sizeof(header) || header.solidCount <= 0 )
+ return;
+
+ int pos = ftell( fp );
+ fseek( fp, 0, SEEK_END );
+ int fileSize = ftell(fp) - pos;
+ fseek( fp, pos, SEEK_SET );
+
+ char *buf = (char *)_alloca( fileSize );
+ fread( buf, fileSize, 1, fp );
+ fclose( fp );
+
+ vcollide_t collide;
+ physcollision->VCollideLoad( &collide, header.solidCount, (const char *)buf, fileSize );
+#if 0
+ Vector start0( -3859.1199, -2050.8674, 64.031250 );
+ Vector end0(-3859.2246, -2051.2817, 64.031250 );
+ Vector modelPosition(-3840,-2068.0000, 82.889099);
+ QAngle modelAngles(0,90,0);
+
+ {
+ Ray_t ray;
+ ray.Init( start0, end0, Vector(-16,-16,0), Vector(16,16,72));
+ trace_t tr;
+ physcollision->TraceBox( ray, collide.solids[0], modelPosition, modelAngles, &tr );
+ Assert(!tr.startsolid);
+ if ( tr.DidHit() )
+ {
+ Ray_t ray2;
+ ray2.Init( tr.endpos, tr.endpos, Vector(-16,-16,0), Vector(16,16,72));
+ trace_t tr2;
+ physcollision->TraceBox( ray2, collide.solids[0], modelPosition, modelAngles, &tr2 );
+ Assert(!tr2.startsolid);
+ }
+ }
+#endif
+#if BENCHMARK_PHY
+ Benchmark_PHY( collide.solids[0] );
+#endif
+ AddVCollideToList( header, collide, params );
+}
+
+void ReadPolyFile (const char *name)
+{
+ char ext[4];
+ Q_ExtractFileExtension( name, ext, 4 );
+
+ bool isPHY = !Q_stricmp( ext, "phy" );
+ if ( isPHY )
+ {
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ if ( physcollision )
+ {
+ phyviewparams_t params;
+ params.Defaults();
+ glNewList (1, GL_COMPILE);
+ ReadPHYFile( name, params );
+ Vector tmp = (params.mins + params.maxs) * 0.5;
+ tmp.CopyToArray(origin);
+ glEndList ();
+ }
+ }
+ else
+ {
+ // Read in polys...
+ ReadPolyFileType(name, 1, false);
+
+ // Make list 3 just the lines... so we can draw outlines
+ ReadPolyFileType(name, 3, true);
+ }
+}
+
+void ReadPortalFile (char *name)
+{
+ FILE *f;
+ int i, numverts;
+ float v[8];
+ int c;
+ int r;
+
+ // For Portal type reading...
+ char szDummy[80];
+ int nNumLeafs;
+ int nNumPortals;
+ int nLeafIndex[2];
+
+ f = fopen (name, "r");
+ if (!f)
+ Error ("Couldn't open %s", name);
+
+ c = 0;
+
+ glNewList (2, GL_COMPILE);
+
+ // Read in header
+ fscanf(f, "%79s\n", szDummy);
+ fscanf(f, "%i\n", &nNumLeafs);
+ fscanf(f, "%i\n", &nNumPortals);
+
+ glLineWidth(1.5);
+
+ while (1)
+ {
+ r = fscanf(f, "%i %i %i ", &numverts, &nLeafIndex[0], &nLeafIndex[1]);
+ if (!r || r == EOF)
+ break;
+
+ glBegin(GL_LINE_LOOP);
+ for (i=0 ; i<numverts ; i++)
+ {
+ r = fscanf (f, "(%f %f %f )\n", &v[0], &v[1],
+ &v[2]);
+ if (!r || (r != 3) || r == EOF)
+ break;
+
+ if ( c == g_nPortalHighlight || nLeafIndex[0] == g_nLeafHighlight || nLeafIndex[1] == g_nLeafHighlight )
+ {
+ glColor4f (1.0, 0.0, 0.0, 1.0);
+ }
+ else
+ {
+ glColor4f (1.0f, 1.0f, 1.0f, 1.0f); // WHITE portals
+ }
+ glVertex3f (v[0], v[1], v[2]);
+ }
+
+ glEnd ();
+ c++;
+ }
+
+ if (f)
+ fclose(f);
+
+ glEndList ();
+}
+
+#define MAX_DISP_COUNT 4096
+static Vector dispPoints[MAX_DISP_COUNT];
+static Vector dispNormals[MAX_DISP_COUNT];
+static int dispPointCount = 0;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+BOOL ReadDisplacementFile( const char *filename )
+{
+ FILE *pFile;
+ int fileCount;
+
+ //
+ // open the file
+ //
+ pFile = fopen( filename, "r" );
+ if( !pFile )
+ Error( "Couldn't open %s", filename );
+
+ //
+ // read data in file
+ //
+ while( 1 )
+ {
+ // overflow test
+ if( dispPointCount >= MAX_DISP_COUNT )
+ break;
+
+ fileCount = fscanf( pFile, "%f %f %f %f %f %f",
+ &dispPoints[dispPointCount][0], &dispPoints[dispPointCount][1], &dispPoints[dispPointCount][2],
+ &dispNormals[dispPointCount][0], &dispNormals[dispPointCount][1], &dispNormals[dispPointCount][2] );
+ dispPointCount++;
+
+ // end of file check
+ if( !fileCount || ( fileCount == EOF ) )
+ break;
+ }
+
+ fclose( pFile );
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void DrawDisplacementData( void )
+{
+ int i, j;
+ int width, halfCount;
+
+ GLUquadricObj *pObject = gluNewQuadric();
+
+ glEnable( GL_DEPTH_TEST );
+
+ for( i = 0; i < dispPointCount; i++ )
+ {
+ // draw a sphere where the point is (in red)
+ glColor3f( 1.0f, 0.0f, 0.0f );
+ glPushMatrix();
+ glTranslatef( dispPoints[i][0], dispPoints[i][1], dispPoints[i][2] );
+ gluSphere( pObject, 5, 5, 5 );
+ glPopMatrix();
+
+ // draw the normal (in yellow)
+ glColor3f( 1.0f, 1.0f, 0.0f );
+ glBegin( GL_LINES );
+ glVertex3f( dispPoints[i][0], dispPoints[i][1], dispPoints[i][2] );
+ glVertex3f( dispPoints[i][0] + ( dispNormals[i][0] * 50.0f ), dispPoints[i][1] + ( dispNormals[i][1] * 50.0f ), dispPoints[i][2] + ( dispNormals[i][2] * 50.0f ) );
+ glEnd();
+ }
+
+ halfCount = dispPointCount / 2;
+
+ width = sqrt( (float)halfCount );
+
+ glDisable( GL_CULL_FACE );
+
+ glColor3f( 0.0f, 0.0f, 1.0f );
+ for( i = 0; i < width - 1; i++ )
+ {
+ for( j = 0; j < width - 1; j++ )
+ {
+ glBegin( GL_POLYGON );
+ glVertex3f( dispPoints[i*width+j][0], dispPoints[i*width+j][1], dispPoints[i*width+j][2] );
+ glVertex3f( dispPoints[(i+1)*width+j][0], dispPoints[(i+1)*width+j][1], dispPoints[(i+1)*width+j][2] );
+ glVertex3f( dispPoints[(i+1)*width+(j+1)][0], dispPoints[(i+1)*width+(j+1)][1], dispPoints[(i+1)*width+(j+1)][2] );
+ glVertex3f( dispPoints[i*width+(j+1)][0], dispPoints[i*width+(j+1)][1], dispPoints[i*width+(j+1)][2] );
+ glEnd();
+ }
+ }
+
+#if 0
+ for( i = 0; i < width - 1; i++ )
+ {
+ for( j = 0; j < width - 1; j++ )
+ {
+ glBegin( GL_POLYGON );
+ glVertex3f( dispPoints[halfCount+(i*width+j)][0], dispPoints[halfCount+(i*width+j)][1], dispPoints[halfCount+(i*width+j)][2] );
+ glVertex3f( dispPoints[halfCount+((i+1)*width+j)][0], dispPoints[halfCount+(i+1)*width+j][1], dispPoints[halfCount+((i+1)*width+j)][2] );
+ glVertex3f( dispPoints[halfCount+((i+1)*width+(j+1))][0], dispPoints[halfCount+(i+1)*width+(j+1)][1], dispPoints[halfCount+((i+1)*width+(j+1))][2] );
+ glVertex3f( dispPoints[halfCount+(i*width+(j+1))][0], dispPoints[halfCount+(i*width+(j+1))][1], dispPoints[halfCount+(i*width+(j+1))][2] );
+ glEnd();
+ }
+ }
+#endif
+
+ glColor3f( 0.0f, 1.0f, 0.0f );
+ for( i = 0; i < width - 1; i++ )
+ {
+ for( j = 0; j < width - 1; j++ )
+ {
+ glBegin( GL_POLYGON );
+ glVertex3f( dispPoints[i*width+j][0] + ( dispNormals[i*width+j][0] * 150.0f ),
+ dispPoints[i*width+j][1] + ( dispNormals[i*width+j][1] * 150.0f ),
+ dispPoints[i*width+j][2] + ( dispNormals[i*width+j][2] * 150.0f ) );
+
+ glVertex3f( dispPoints[(i+1)*width+j][0] + ( dispNormals[(i+1)*width+j][0] * 150.0f ),
+ dispPoints[(i+1)*width+j][1] + ( dispNormals[(i+1)*width+j][1] * 150.0f ),
+ dispPoints[(i+1)*width+j][2] + ( dispNormals[(i+1)*width+j][2] * 150.0f ) );
+
+ glVertex3f( dispPoints[(i+1)*width+(j+1)][0] + ( dispNormals[(i+1)*width+(j+1)][0] * 150.0f ),
+ dispPoints[(i+1)*width+(j+1)][1] + ( dispNormals[(i+1)*width+(j+1)][1] * 150.0f ),
+ dispPoints[(i+1)*width+(j+1)][2] + ( dispNormals[(i+1)*width+(j+1)][2] * 150.0f ) );
+
+ glVertex3f( dispPoints[i*width+(j+1)][0] + ( dispNormals[i*width+(j+1)][0] * 150.0f ),
+ dispPoints[i*width+(j+1)][1] + ( dispNormals[i*width+(j+1)][1] * 150.0f ),
+ dispPoints[i*width+(j+1)][2] + ( dispNormals[i*width+(j+1)][2] * 150.0f ) );
+ glEnd();
+ }
+ }
+
+ glDisable( GL_DEPTH_TEST );
+
+ glColor3f( 0.0f, 0.0f, 1.0f );
+ for( i = 0; i < width - 1; i++ )
+ {
+ for( j = 0; j < width - 1; j++ )
+ {
+ glBegin( GL_LINE_LOOP );
+ glVertex3f( dispPoints[i*width+j][0] + ( dispNormals[i*width+j][0] * 150.0f ),
+ dispPoints[i*width+j][1] + ( dispNormals[i*width+j][1] * 150.0f ),
+ dispPoints[i*width+j][2] + ( dispNormals[i*width+j][2] * 150.0f ) );
+
+ glVertex3f( dispPoints[(i+1)*width+j][0] + ( dispNormals[(i+1)*width+j][0] * 150.0f ),
+ dispPoints[(i+1)*width+j][1] + ( dispNormals[(i+1)*width+j][1] * 150.0f ),
+ dispPoints[(i+1)*width+j][2] + ( dispNormals[(i+1)*width+j][2] * 150.0f ) );
+
+ glVertex3f( dispPoints[(i+1)*width+(j+1)][0] + ( dispNormals[(i+1)*width+(j+1)][0] * 150.0f ),
+ dispPoints[(i+1)*width+(j+1)][1] + ( dispNormals[(i+1)*width+(j+1)][1] * 150.0f ),
+ dispPoints[(i+1)*width+(j+1)][2] + ( dispNormals[(i+1)*width+(j+1)][2] * 150.0f ) );
+
+ glVertex3f( dispPoints[i*width+(j+1)][0] + ( dispNormals[i*width+(j+1)][0] * 150.0f ),
+ dispPoints[i*width+(j+1)][1] + ( dispNormals[i*width+(j+1)][1] * 150.0f ),
+ dispPoints[i*width+(j+1)][2] + ( dispNormals[i*width+(j+1)][2] * 150.0f ) );
+ glEnd();
+ }
+ }
+
+
+ gluDeleteQuadric( pObject );
+}
+
+
+//=====================================================================
+
+BOOL bSetupPixelFormat(HDC hDC)
+{
+ static PIXELFORMATDESCRIPTOR pfd = {
+ sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
+ 1, // version number
+ PFD_DRAW_TO_WINDOW | // support window
+ PFD_SUPPORT_OPENGL | // support OpenGL
+ PFD_DOUBLEBUFFER, // double buffered
+ PFD_TYPE_RGBA, // RGBA type
+ 24, // 24-bit color depth
+ 0, 0, 0, 0, 0, 0, // color bits ignored
+ 0, // no alpha buffer
+ 0, // shift bit ignored
+ 0, // no accumulation buffer
+ 0, 0, 0, 0, // accum bits ignored
+ 32, // 32-bit z-buffer
+ 0, // no stencil buffer
+ 0, // no auxiliary buffer
+ PFD_MAIN_PLANE, // main layer
+ 0, // reserved
+ 0, 0, 0 // layer masks ignored
+ };
+
+ int pixelformat = 0;
+
+ if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
+ Error ("ChoosePixelFormat failed");
+
+ if (!SetPixelFormat(hDC, pixelformat, &pfd))
+ Error ("SetPixelFormat failed");
+
+ return TRUE;
+}
+
+/*
+============
+CameraWndProc
+============
+*/
+LONG WINAPI WCam_WndProc (
+ HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ LONG lRet = 1;
+ RECT rect;
+
+ GetClientRect(hWnd, &rect);
+
+ switch (uMsg)
+ {
+ case WM_CREATE:
+ {
+ camdc = GetDC(hWnd);
+ bSetupPixelFormat(camdc);
+
+ baseRC = wglCreateContext( camdc );
+ if (!baseRC)
+ Error ("wglCreateContext failed");
+ if (!wglMakeCurrent( camdc, baseRC ))
+ Error ("wglMakeCurrent failed");
+ glCullFace(GL_FRONT);
+ glEnable(GL_CULL_FACE);
+ }
+ break;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+
+ BeginPaint(hWnd, &ps);
+ if (!wglMakeCurrent( camdc, baseRC ))
+ Error ("wglMakeCurrent failed");
+ Draw ();
+ SwapBuffers(camdc);
+ EndPaint(hWnd, &ps);
+ }
+ break;
+
+ case WM_KEYDOWN:
+ KeyDown (wParam);
+ AppKeyDown( wParam );
+ break;
+
+ case WM_KEYUP:
+ AppKeyUp( wParam );
+ break;
+
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONDOWN:
+ SetCapture (camerawindow);
+ ShowCursor( FALSE );
+ g_Capture = TRUE;
+ break;
+
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_LBUTTONUP:
+ if (! (wParam & (MK_LBUTTON|MK_RBUTTON|MK_MBUTTON)))
+ {
+ g_Capture = FALSE;
+ ReleaseCapture ();
+ ShowCursor( TRUE );
+ }
+ break;
+
+ case WM_SIZE:
+ InvalidateRect(camerawindow, NULL, false);
+ break;
+ case WM_NCCALCSIZE:// don't let windows copy pixels
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ return WVR_REDRAW;
+ case WM_CLOSE:
+ /* call destroy window to cleanup and go away */
+ DestroyWindow (hWnd);
+ break;
+
+ case WM_DESTROY:
+ {
+ HGLRC hRC;
+ HDC hDC;
+
+ /* release and free the device context and rendering context */
+ hRC = wglGetCurrentContext();
+ hDC = wglGetCurrentDC();
+
+ wglMakeCurrent(NULL, NULL);
+
+ if (hRC)
+ wglDeleteContext(hRC);
+ if (hDC)
+ ReleaseDC(hWnd, hDC);
+
+ PostQuitMessage (0);
+ }
+ break;
+
+ default:
+ /* pass all unhandled messages to DefWindowProc */
+ lRet = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ break;
+ }
+
+ /* return 1 if handled message, 0 if not */
+ return lRet;
+}
+
+
+/*
+==============
+WCam_Register
+==============
+*/
+void WCam_Register (HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+
+ /* Register the camera class */
+ memset (&wc, 0, sizeof(wc));
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WCam_WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = 0;
+ wc.hCursor = LoadCursor (NULL,IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = 0;
+ wc.lpszClassName = "camera";
+
+ if (!RegisterClass (&wc) )
+ Error ("WCam_Register: failed");
+}
+
+
+void WCam_Create (HINSTANCE hInstance)
+{
+ // Center it
+ int nScx, nScy;
+ int w, h;
+ int x, y;
+
+ WCam_Register (hInstance);
+
+ w = ::width;
+ h = ::height;
+
+ nScx = GetSystemMetrics(SM_CXSCREEN);
+ nScy = GetSystemMetrics(SM_CYSCREEN);
+
+
+ x = (nScx - w)/2;
+ y = (nScy - h)/2;
+
+ camerawindow = CreateWindow ("camera" ,
+ "Camera View",
+ WS_OVERLAPPED |
+ WS_CAPTION |
+ WS_SYSMENU |
+ WS_THICKFRAME |
+ WS_MAXIMIZEBOX |
+ WS_CLIPSIBLINGS |
+ WS_CLIPCHILDREN,
+
+ x,
+ y,
+ w,
+ h, // size
+
+ NULL, // parent window
+ 0, // no menu
+ hInstance,
+ 0);
+ if (!camerawindow)
+ Error ("Couldn't create camerawindow");
+
+ ShowWindow (camerawindow, SW_SHOWDEFAULT);
+}
+
+
+void AppKeyDown( int key )
+{
+ key &= 0xFF;
+
+ g_Keys[key] = 0x03; // add debounce bit
+}
+
+void AppKeyUp( int key )
+{
+ key &= 0xFF;
+
+ g_Keys[key] &= 0x02;
+}
+
+void AppRender( void )
+{
+ static double lastTime = 0;
+ double time = timeGetTime() * 0.001f;
+ double frametime = time - lastTime;
+
+ // clamp too large frames (like first frame)
+ if ( frametime > 0.2 )
+ frametime = 0.2;
+ lastTime = time;
+
+ if (!wglMakeCurrent( camdc, baseRC ))
+ Error ("wglMakeCurrent failed");
+
+ Cam_Update( frametime );
+
+ if (g_Update)
+ {
+ Draw ();
+ SwapBuffers(camdc);
+ g_Update = FALSE;
+ }
+ else
+ {
+ Sleep( 1.0 );
+ }
+}
+
+SpewRetval_t Sys_SpewFunc( SpewType_t type, const char *pMsg )
+{
+ OutputDebugString( pMsg );
+ if( type == SPEW_ASSERT )
+ return SPEW_DEBUGGER;
+ else if( type == SPEW_ERROR )
+ return SPEW_ABORT;
+ else
+ return SPEW_CONTINUE;
+}
+
+
+/*
+==================
+WinMain
+
+==================
+*/
+int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance
+ ,LPSTR lpCmdLine, int nCmdShow)
+{
+ CommandLine()->CreateCmdLine( Plat_GetCommandLine() );
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+ MSG msg;
+
+ if (!lpCmdLine || !lpCmdLine[0])
+ Error ("No file specified");
+
+ main_instance = hInstance;
+
+ WCam_Create (hInstance);
+
+ // Last argument is the file name
+ const char *pFileName = CommandLine()->GetParm( CommandLine()->ParmCount() - 1 );
+ CmdLib_InitFileSystem( pFileName );
+
+ if ( CommandLine()->CheckParm( "-portal") )
+ {
+ g_bReadPortals = 1;
+ g_nPortalHighlight = CommandLine()->ParmValue( "-portalhighlight", -1 );
+ g_nLeafHighlight = CommandLine()->ParmValue( "-leafhighlight", -1 );
+ }
+ g_flMovementSpeed = CommandLine()->ParmValue( "-speed", 320 );
+
+ if( CommandLine()->CheckParm( "-disp") )
+ {
+ ReadDisplacementFile( pFileName );
+ g_bDisp = TRUE;
+ }
+ SpewOutputFunc( Sys_SpewFunc );
+
+ // Any chunk of original left is the filename.
+ if (pFileName && pFileName[0] && !g_bDisp )
+ {
+ ReadPolyFile( pFileName );
+ }
+
+ if (g_bReadPortals)
+ {
+ // Copy file again and this time look for the . from .gl? so we can concatenate .prt
+ // and open the portal file.
+ char szTempCmd[MAX_PATH];
+ strcpy(szTempCmd, pFileName);
+ char *pTmp = szTempCmd;
+ while (pTmp && *pTmp && *pTmp != '.')
+ {
+ pTmp++;
+ }
+
+ *pTmp = '\0';
+ strcat(szTempCmd, ".prt");
+
+ ReadPortalFile(szTempCmd);
+ };
+
+ /* main window message loop */
+ while (g_Active)
+ {
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ AppRender();
+ }
+
+ /* return success of application */
+ return TRUE;
+}
+
diff --git a/mp/src/utils/height2normal/height2normal-2010.vcxproj b/mp/src/utils/height2normal/height2normal-2010.vcxproj new file mode 100644 index 00000000..40f3288c --- /dev/null +++ b/mp/src/utils/height2normal/height2normal-2010.vcxproj @@ -0,0 +1,241 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Height2normal</ProjectName>
+ <ProjectGuid>{129A563E-9F48-79D9-E0C5-EE2DAF7FEAB7}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>height2normal</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>height2normal</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 height2normal.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;_DEBUG;_WIN32;_CONSOLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\height2normal;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\height2normal.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/height2normal.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 height2normal.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;_DEBUG;_WIN32;_CONSOLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\height2normal;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\height2normal.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/height2normal.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib" />
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="height2normal.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/height2normal/height2normal-2010.vcxproj.filters b/mp/src/utils/height2normal/height2normal-2010.vcxproj.filters new file mode 100644 index 00000000..192274bd --- /dev/null +++ b/mp/src/utils/height2normal/height2normal-2010.vcxproj.filters @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="height2normal.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/height2normal/height2normal.cpp b/mp/src/utils/height2normal/height2normal.cpp new file mode 100644 index 00000000..f0dff189 --- /dev/null +++ b/mp/src/utils/height2normal/height2normal.cpp @@ -0,0 +1,343 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#include <stdio.h>
+#include <stdlib.h>
+#include <direct.h>
+#include "bitmap/imageformat.h"
+#include "tier1/strtools.h"
+#include "mathlib/mathlib.h"
+#include "bitmap/TGAWriter.h"
+#include "bitmap/TGALoader.h"
+#include <math.h>
+#include <conio.h>
+#include "tier1/utlbuffer.h"
+#include "tier2/tier2.h"
+#include "filesystem.h"
+
+static bool g_NoPause = false;
+static bool g_Quiet = false;
+
+static void Pause( void )
+{
+ if( !g_NoPause )
+ {
+ printf( "Hit a key to continue\n" );
+ getch();
+ }
+}
+
+static bool ImageRGBA8888HasAlpha( unsigned char *pImage, int numTexels )
+{
+ int i;
+ for( i = 0; i < numTexels; i++ )
+ {
+ if( pImage[i*4+3] != 255 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool GetKeyValueFromBuffer( CUtlBuffer &buf, char **key, char **val )
+{
+ char stringBuf[2048];
+ while( buf.IsValid() )
+ {
+ buf.GetLine( stringBuf, sizeof(stringBuf) );
+ char *scan = stringBuf;
+ // search for the first quote for the key.
+ while( 1 )
+ {
+ if( *scan == '\"' )
+ {
+ *key = ++scan;
+ break;
+ }
+ if( *scan == '#' )
+ {
+ goto next_line; // comment
+ }
+ if( *scan == '\0' )
+ {
+ goto next_line; // end of line.
+ }
+ scan++;
+ }
+ // read the key until another quote.
+ while( 1 )
+ {
+ if( *scan == '\"' )
+ {
+ *scan = '\0';
+ scan++;
+ break;
+ }
+ if( *scan == '\0' )
+ {
+ goto next_line;
+ }
+ scan++;
+ }
+ // search for the first quote for the value.
+ while( 1 )
+ {
+ if( *scan == '\"' )
+ {
+ *val = ++scan;
+ break;
+ }
+ if( *scan == '#' )
+ {
+ goto next_line; // comment
+ }
+ if( *scan == '\0' )
+ {
+ goto next_line; // end of line.
+ }
+ scan++;
+ }
+ // read the value until another quote.
+ while( 1 )
+ {
+ if( *scan == '\"' )
+ {
+ *scan = '\0';
+ scan++;
+ // got a key and a value, so get the hell out of here.
+ return true;
+ }
+ if( *scan == '\0' )
+ {
+ goto next_line;
+ }
+ scan++;
+ }
+next_line:
+ ;
+ }
+ return false;
+}
+
+static void LoadConfigFile( const char *pFileName, float *bumpScale, int *startFrame, int *endFrame )
+{
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ if ( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) )
+ {
+ fprintf( stderr, "Can't open: %s\n", pFileName );
+ Pause();
+ exit( -1 );
+ }
+
+ char *key = NULL;
+ char *val = NULL;
+ while( GetKeyValueFromBuffer( buf, &key, &val ) )
+ {
+ if( stricmp( key, "bumpscale" ) == 0 )
+ {
+ *bumpScale = atof( val );
+ }
+ if( stricmp( key, "startframe" ) == 0 )
+ {
+ *startFrame = atoi( val );
+ }
+ else if( stricmp( key, "endframe" ) == 0 )
+ {
+ *endFrame = atoi( val );
+ }
+ }
+}
+
+static void Usage()
+{
+ fprintf( stderr, "Usage: height2normal [-nopause] [-quiet] tex1_normal.txt tex2_normal.txt . . .\n" );
+ fprintf( stderr, "-quiet : don't print anything out, don't pause for input\n" );
+ fprintf( stderr, "-nopause : don't pause for input\n" );
+ Pause();
+ exit( -1 );
+}
+
+void ProcessFiles( const char *pNormalFileNameWithoutExtension,
+ int startFrame, int endFrame,
+ float bumpScale )
+{
+ static char heightTGAFileName[1024];
+ static char normalTGAFileName[1024];
+ static char buf[1024];
+ bool animated = !( startFrame == -1 || endFrame == -1 );
+ int numFrames = endFrame - startFrame + 1;
+ int frameID;
+
+ for( frameID = 0; frameID < numFrames; frameID++ )
+ {
+ if( animated )
+ {
+ sprintf( normalTGAFileName, "%s%03d.tga", pNormalFileNameWithoutExtension, frameID + startFrame );
+ }
+ else
+ {
+ sprintf( normalTGAFileName, "%s.tga", pNormalFileNameWithoutExtension );
+ }
+ if( !Q_stristr( pNormalFileNameWithoutExtension, "_normal" ) )
+ {
+ fprintf( stderr, "ERROR: config file name must end in _normal.txt\n" );
+ return;
+ }
+
+ strcpy( buf, pNormalFileNameWithoutExtension );
+
+ // Strip '_normal' off the end because we're looking for '_height'
+ char *pcUnderscore = Q_stristr( buf, "_normal" );
+ *pcUnderscore = NULL;
+
+ if( animated )
+ {
+ sprintf( heightTGAFileName, "%s_height%03d.tga", buf, frameID + startFrame );
+ }
+ else
+ {
+ sprintf( heightTGAFileName, "%s_height.tga", buf );
+ }
+
+ enum ImageFormat imageFormat;
+ int width, height;
+ float sourceGamma;
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( heightTGAFileName, NULL, buf ) )
+ {
+ fprintf( stderr, "%s not found\n", heightTGAFileName );
+ return;
+ }
+
+ if ( !TGALoader::GetInfo( buf, &width, &height, &imageFormat, &sourceGamma ) )
+ {
+ fprintf( stderr, "error in %s\n", heightTGAFileName );
+ return;
+ }
+
+ int memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_IA88, false );
+ unsigned char *pImageIA88 = new unsigned char[memRequired];
+
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ TGALoader::Load( pImageIA88, buf, width, height, IMAGE_FORMAT_IA88, sourceGamma, false );
+
+ memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_RGBA8888, false );
+ unsigned char *pImageRGBA8888 = new unsigned char[memRequired];
+ ImageLoader::ConvertIA88ImageToNormalMapRGBA8888( pImageIA88, width, height, pImageRGBA8888, bumpScale );
+
+ CUtlBuffer normalBuf;
+ ImageLoader::NormalizeNormalMapRGBA8888( pImageRGBA8888, width * height );
+ if( ImageRGBA8888HasAlpha( pImageRGBA8888, width * height ) )
+ {
+ TGAWriter::WriteToBuffer( pImageRGBA8888, normalBuf, width, height, IMAGE_FORMAT_RGBA8888, IMAGE_FORMAT_RGBA8888 );
+ }
+ else
+ {
+ memRequired = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_RGB888, false );
+ unsigned char *pImageRGB888 = new unsigned char[memRequired];
+ ImageLoader::ConvertImageFormat( pImageRGBA8888, IMAGE_FORMAT_RGBA8888,
+ pImageRGB888, IMAGE_FORMAT_RGB888, width, height, 0, 0 );
+ TGAWriter::WriteToBuffer( pImageRGB888, normalBuf, width, height, IMAGE_FORMAT_RGB888, IMAGE_FORMAT_RGB888 );
+ delete [] pImageRGB888;
+ }
+ if ( !g_pFullFileSystem->WriteFile( normalTGAFileName, NULL, normalBuf ) )
+ {
+ fprintf( stderr, "unable to write %s\n", normalTGAFileName );
+ return;
+ }
+ delete [] pImageIA88;
+ delete [] pImageRGBA8888;
+ }
+}
+
+int main( int argc, char **argv )
+{
+ if( argc < 2 )
+ {
+ Usage();
+ }
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+ InitDefaultFileSystem();
+
+ int i = 1;
+ while( i < argc )
+ {
+ if( stricmp( argv[i], "-quiet" ) == 0 )
+ {
+ i++;
+ g_Quiet = true;
+ g_NoPause = true; // no point in pausing if we aren't going to print anything out.
+ }
+ if( stricmp( argv[i], "-nopause" ) == 0 )
+ {
+ i++;
+ g_NoPause = true;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ char pCurrentDirectory[MAX_PATH];
+ if ( _getcwd( pCurrentDirectory, sizeof(pCurrentDirectory) ) == NULL )
+ {
+ fprintf( stderr, "Unable to get the current directory\n" );
+ return -1;
+ }
+
+ Q_FixSlashes( pCurrentDirectory );
+ Q_StripTrailingSlash( pCurrentDirectory );
+
+ for( ; i < argc; i++ )
+ {
+ static char normalFileNameWithoutExtension[1024];
+ char *pFileName;
+ if ( !Q_IsAbsolutePath( argv[i] ) )
+ {
+ Q_snprintf( normalFileNameWithoutExtension, sizeof(normalFileNameWithoutExtension), "%s\\%s", pCurrentDirectory, argv[i] );
+ pFileName = normalFileNameWithoutExtension;
+ }
+ else
+ {
+ pFileName = argv[i];
+ }
+
+ if( !g_Quiet )
+ {
+ printf( "file: %s\n", pFileName );
+ }
+ float bumpScale = -1.0f;
+ int startFrame = -1;
+ int endFrame = -1;
+ LoadConfigFile( pFileName, &bumpScale, &startFrame, &endFrame );
+ if( bumpScale == -1.0f )
+ {
+ fprintf( stderr, "Must specify \"bumpscale\" in config file\n" );
+ Pause();
+ continue;
+ }
+ if( ( startFrame == -1 && endFrame != -1 ) ||
+ ( startFrame != -1 && endFrame == -1 ) )
+ {
+ fprintf( stderr, "ERROR: If you use startframe, you must use endframe, and vice versa.\n" );
+ Pause();
+ continue;
+ }
+ if( !g_Quiet )
+ {
+ printf( "\tbumpscale: %f\n", bumpScale );
+ }
+
+ Q_StripExtension( pFileName, normalFileNameWithoutExtension, sizeof( normalFileNameWithoutExtension ) );
+ ProcessFiles( normalFileNameWithoutExtension,
+ startFrame, endFrame,
+ bumpScale );
+ }
+ return 0;
+}
diff --git a/mp/src/utils/motionmapper/motionmapper-2010.vcxproj b/mp/src/utils/motionmapper/motionmapper-2010.vcxproj new file mode 100644 index 00000000..ba84e933 --- /dev/null +++ b/mp/src/utils/motionmapper/motionmapper-2010.vcxproj @@ -0,0 +1,288 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Motionmapper</ProjectName>
+ <ProjectGuid>{C805838C-256D-6672-3417-589B6AF7D95E}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>motionmapper</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>motionmapper</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 motionmapper.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\nvtristriplib;..\..\Game_Shared</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\motionmapper;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);winmm.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\motionmapper.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/motionmapper.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 motionmapper.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\nvtristriplib;..\..\Game_Shared</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\motionmapper;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);winmm.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\motionmapper.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/motionmapper.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\nvtristrip.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h" />
+ <ClInclude Include="..\..\public\tier0\basetypes.h" />
+ <ClInclude Include="..\..\public\tier1\characterset.h" />
+ <ClInclude Include="..\common\cmdlib.h" />
+ <ClInclude Include="..\..\public\Color.h" />
+ <ClInclude Include="..\..\public\tier0\commonmacros.h" />
+ <ClInclude Include="..\..\public\mathlib\compressed_vector.h" />
+ <ClInclude Include="..\..\public\tier0\dbg.h" />
+ <ClInclude Include="..\..\public\tier0\fasttimer.h" />
+ <ClInclude Include="..\..\public\filesystem.h" />
+ <ClInclude Include="..\..\public\filesystem_helpers.h" />
+ <ClInclude Include="..\common\filesystem_tools.h" />
+ <ClInclude Include="..\..\public\appframework\IAppSystem.h" />
+ <ClInclude Include="..\..\public\tier0\icommandline.h" />
+ <ClInclude Include="..\..\public\vstdlib\IKeyValuesSystem.h" />
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="..\..\public\tier1\KeyValues.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ <ClInclude Include="..\..\public\tier0\mem.h" />
+ <ClInclude Include="..\..\public\tier0\memalloc.h" />
+ <ClInclude Include="..\..\public\tier0\memdbgoff.h" />
+ <ClInclude Include="..\..\public\tier0\memdbgon.h" />
+ <ClInclude Include="motionmapper.h" />
+ <ClInclude Include="..\..\public\tier0\platform.h" />
+ <ClInclude Include="..\..\public\tier0\protected_things.h" />
+ <ClInclude Include="..\common\scriplib.h" />
+ <ClInclude Include="..\..\public\string_t.h" />
+ <ClInclude Include="..\..\public\tier1\strtools.h" />
+ <ClInclude Include="..\..\public\studio.h" />
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h" />
+ <ClInclude Include="..\..\public\tier1\utldict.h" />
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h" />
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h" />
+ <ClInclude Include="..\..\public\tier1\utlvector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector2d.h" />
+ <ClInclude Include="..\..\public\mathlib\vector4d.h" />
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\cmdlib.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="..\..\public\filesystem_init.cpp" />
+ <ClCompile Include="..\common\filesystem_tools.cpp" />
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="motionmapper.cpp" />
+ <ClCompile Include="..\common\scriplib.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/motionmapper/motionmapper-2010.vcxproj.filters b/mp/src/utils/motionmapper/motionmapper-2010.vcxproj.filters new file mode 100644 index 00000000..15d71252 --- /dev/null +++ b/mp/src/utils/motionmapper/motionmapper-2010.vcxproj.filters @@ -0,0 +1,188 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\nvtristrip.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\basetypes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\characterset.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\cmdlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\Color.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\commonmacros.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\compressed_vector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\dbg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\fasttimer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem_helpers.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\filesystem_tools.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\appframework\IAppSystem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\icommandline.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\IKeyValuesSystem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\KeyValues.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\mem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memalloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memdbgoff.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memdbgon.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="motionmapper.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\platform.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\protected_things.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\scriplib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\string_t.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\strtools.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\studio.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utldict.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlvector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector2d.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector4d.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\cmdlib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_init.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\filesystem_tools.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="motionmapper.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\scriplib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/motionmapper/motionmapper.cpp b/mp/src/utils/motionmapper/motionmapper.cpp new file mode 100644 index 00000000..6961e8ec --- /dev/null +++ b/mp/src/utils/motionmapper/motionmapper.cpp @@ -0,0 +1,3272 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <math.h>
+#include "filesystem_tools.h"
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "mathlib/mathlib.h"
+#define EXTERN
+#include "studio.h"
+#include "motionmapper.h"
+#include "tier1/strtools.h"
+#include "tier0/icommandline.h"
+#include "utldict.h"
+#include <windows.h>
+#include "UtlBuffer.h"
+#include "utlsymbol.h"
+
+bool g_quiet = false;
+bool g_verbose = false;
+char g_outfile[1024];
+bool uselogfile = false;
+
+char g_szFilename[1024];
+FILE *g_fpInput;
+char g_szLine[4096];
+int g_iLinecount;
+
+bool g_bZBrush = false;
+bool g_bGaveMissingBoneWarning = false;
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : depth -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void vprint( int depth, const char *fmt, ... )
+{
+ char string[ 8192 ];
+ va_list va;
+ va_start( va, fmt );
+ V_vsprintf_safe( string, fmt, va );
+ va_end( va );
+
+ FILE *fp = NULL;
+
+ if ( uselogfile )
+ {
+ fp = fopen( "log.txt", "ab" );
+ }
+
+ while ( depth-- > 0 )
+ {
+ vprint( 0, " " );
+ OutputDebugString( " " );
+ if ( fp )
+ {
+ fprintf( fp, " " );
+ }
+ }
+
+ ::printf( "%s", string );
+ OutputDebugString( string );
+
+ if ( fp )
+ {
+ char *p = string;
+ while ( *p )
+ {
+ if ( *p == '\n' )
+ {
+ fputc( '\r', fp );
+ }
+ fputc( *p, fp );
+ p++;
+ }
+ fclose( fp );
+ }
+}
+
+
+int k_memtotal;
+void *kalloc( int num, int size )
+{
+ // vprint( 0, "calloc( %d, %d )\n", num, size );
+ // vprint( 0, "%d ", num * size );
+ k_memtotal += num * size;
+ return calloc( num, size );
+}
+
+void kmemset( void *ptr, int value, int size )
+{
+ // vprint( 0, "kmemset( %x, %d, %d )\n", ptr, value, size );
+ memset( ptr, value, size );
+ return;
+}
+
+static bool g_bFirstWarning = true;
+
+void MdlWarning( const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ if (g_quiet)
+ {
+ if (g_bFirstWarning)
+ {
+ vprint( 0, "%s :\n", fullpath );
+ g_bFirstWarning = false;
+ }
+ vprint( 0, "\t");
+ }
+
+ vprint( 0, "WARNING: ");
+ va_start( args, fmt );
+ vprint( 0, fmt, args );
+}
+
+
+void MdlError( char const *fmt, ... )
+{
+ va_list args;
+
+ if (g_quiet)
+ {
+ if (g_bFirstWarning)
+ {
+ vprint( 0, "%s :\n", fullpath );
+ g_bFirstWarning = false;
+ }
+ vprint( 0, "\t");
+ }
+
+ vprint( 0, "ERROR: ");
+ va_start( args, fmt );
+ vprint( 0, fmt, args );
+
+ exit( -1 );
+}
+
+int OpenGlobalFile( char *src )
+{
+ int time1;
+ char filename[1024];
+
+ // local copy of string
+ strcpy( filename, ExpandPath( src ) );
+
+ // Ummm, path sanity checking
+ int pathLength;
+ int numBasePaths = CmdLib_GetNumBasePaths();
+ // This is kinda gross. . . doing the same work in cmdlib on SafeOpenRead.
+ if( CmdLib_HasBasePath( filename, pathLength ) )
+ {
+ char tmp[1024];
+ int i;
+ for( i = 0; i < numBasePaths; i++ )
+ {
+ strcpy( tmp, CmdLib_GetBasePath( i ) );
+ strcat( tmp, filename + pathLength );
+
+ time1 = FileTime( tmp );
+ if( time1 != -1 )
+ {
+ if ((g_fpInput = fopen(tmp, "r")) == 0)
+ {
+ MdlWarning( "reader: could not open file '%s'\n", src );
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+ else
+ {
+ time1 = FileTime (filename);
+ if (time1 == -1)
+ return 0;
+
+ // Whoohooo, FOPEN!
+ if ((g_fpInput = fopen(filename, "r")) == 0)
+ {
+ MdlWarning( "reader: could not open file '%s'\n", src );
+ return 0;
+ }
+
+ return 1;
+ }
+}
+
+bool IsEnd( char const* pLine )
+{
+ if (strncmp( "end", pLine, 3 ) != 0)
+ return false;
+ return (pLine[3] == '\0') || (pLine[3] == '\n');
+}
+
+
+//Wrong name for the use of it.
+void scale_vertex( Vector &org )
+{
+ org[0] = org[0] * g_currentscale;
+ org[1] = org[1] * g_currentscale;
+ org[2] = org[2] * g_currentscale;
+}
+
+
+void clip_rotations( RadianEuler& rot )
+{
+ int j;
+ // clip everything to : -M_PI <= x < M_PI
+
+ for (j = 0; j < 3; j++) {
+ while (rot[j] >= M_PI)
+ rot[j] -= M_PI*2;
+ while (rot[j] < -M_PI)
+ rot[j] += M_PI*2;
+ }
+}
+
+
+void clip_rotations( Vector& rot )
+{
+ int j;
+ // clip everything to : -180 <= x < 180
+
+ for (j = 0; j < 3; j++) {
+ while (rot[j] >= 180)
+ rot[j] -= 180*2;
+ while (rot[j] < -180)
+ rot[j] += 180*2;
+ }
+}
+
+
+void Build_Reference( s_source_t *psource)
+{
+ int i, parent;
+ Vector angle;
+
+ for (i = 0; i < psource->numbones; i++)
+ {
+ matrix3x4_t m;
+ AngleMatrix( psource->rawanim[0][i].rot, m );
+ m[0][3] = psource->rawanim[0][i].pos[0];
+ m[1][3] = psource->rawanim[0][i].pos[1];
+ m[2][3] = psource->rawanim[0][i].pos[2];
+
+ parent = psource->localBone[i].parent;
+ if (parent == -1)
+ {
+ // scale the done pos.
+ // calc rotational matrices
+ MatrixCopy( m, psource->boneToPose[i] );
+ }
+ else
+ {
+ // calc compound rotational matrices
+ // FIXME : Hey, it's orthogical so inv(A) == transpose(A)
+ ConcatTransforms( psource->boneToPose[parent], m, psource->boneToPose[i] );
+ }
+ // vprint( 0, "%3d %f %f %f\n", i, psource->bonefixup[i].worldorg[0], psource->bonefixup[i].worldorg[1], psource->bonefixup[i].worldorg[2] );
+ /*
+ AngleMatrix( angle, m );
+ vprint( 0, "%8.4f %8.4f %8.4f\n", m[0][0], m[1][0], m[2][0] );
+ vprint( 0, "%8.4f %8.4f %8.4f\n", m[0][1], m[1][1], m[2][1] );
+ vprint( 0, "%8.4f %8.4f %8.4f\n", m[0][2], m[1][2], m[2][2] );
+ */
+ }
+}
+
+int Grab_Nodes( s_node_t *pnodes )
+{
+ //
+ // s_node_t structure: index is index!!
+ //
+ int index;
+ char name[1024];
+ int parent;
+ int numbones = 0;
+
+ // Init parent to none
+ for (index = 0; index < MAXSTUDIOSRCBONES; index++)
+ {
+ pnodes[index].parent = -1;
+ }
+
+ // March through nodes lines
+ while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
+ {
+ g_iLinecount++;
+ // get tokens
+ if (sscanf( g_szLine, "%d \"%[^\"]\" %d", &index, name, &parent ) == 3)
+ {
+ // check for duplicated bones
+ /*
+ if (strlen(pnodes[index].name) != 0)
+ {
+ MdlError( "bone \"%s\" exists more than once\n", name );
+ }
+ */
+ // copy name to struct array
+ V_strcpy_safe( pnodes[index].name, name );
+ // set parent into struct array
+ pnodes[index].parent = parent;
+ // increment numbones
+ if (index > numbones)
+ {
+ numbones = index;
+ }
+ }
+ else
+ {
+ return numbones + 1;
+ }
+ }
+ MdlError( "Unexpected EOF at line %d\n", g_iLinecount );
+ return 0;
+}
+
+void Grab_Vertexanimation( s_source_t *psource )
+{
+ char cmd[1024];
+ int index;
+ Vector pos;
+ Vector normal;
+ int t = -1;
+ int count = 0;
+ static s_vertanim_t tmpvanim[MAXSTUDIOVERTS*4];
+
+ while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
+ {
+ g_iLinecount++;
+ if (sscanf( g_szLine, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &normal[0], &normal[1], &normal[2] ) == 7)
+ {
+ if (psource->startframe < 0)
+ {
+ MdlError( "Missing frame start(%d) : %s", g_iLinecount, g_szLine );
+ }
+
+ if (t < 0)
+ {
+ MdlError( "VTA Frame Sync (%d) : %s", g_iLinecount, g_szLine );
+ }
+
+ tmpvanim[count].vertex = index;
+ VectorCopy( pos, tmpvanim[count].pos );
+ VectorCopy( normal, tmpvanim[count].normal );
+ count++;
+
+ if (index >= psource->numvertices)
+ psource->numvertices = index + 1;
+ }
+ else
+ {
+ // flush data
+
+ if (count)
+ {
+ psource->numvanims[t] = count;
+
+ psource->vanim[t] = (s_vertanim_t *)kalloc( count, sizeof( s_vertanim_t ) );
+
+ memcpy( psource->vanim[t], tmpvanim, count * sizeof( s_vertanim_t ) );
+ }
+ else if (t > 0)
+ {
+ psource->numvanims[t] = 0;
+ }
+
+ // next command
+ if (sscanf( g_szLine, "%1023s %d", cmd, &index ))
+ {
+ if (strcmp( cmd, "time" ) == 0)
+ {
+ t = index;
+ count = 0;
+
+ if (t < psource->startframe)
+ {
+ MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine );
+ }
+ if (t > psource->endframe)
+ {
+ MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine );
+ }
+
+ t -= psource->startframe;
+ }
+ else if (strcmp( cmd, "end") == 0)
+ {
+ psource->numframes = psource->endframe - psource->startframe + 1;
+ return;
+ }
+ else
+ {
+ MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
+ }
+
+ }
+ else
+ {
+ MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
+ }
+ }
+ }
+ MdlError( "unexpected EOF: %s\n", psource->filename );
+}
+
+void Grab_Animation( s_source_t *psource )
+{
+ Vector pos;
+ RadianEuler rot;
+ char cmd[1024];
+ int index;
+ int t = -99999999;
+ int size;
+
+ // Init startframe
+ psource->startframe = -1;
+
+ // size per frame
+ size = psource->numbones * sizeof( s_bone_t );
+
+ // march through animation
+ while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
+ {
+ // linecount
+ g_iLinecount++;
+ // split if big enoough
+ if (sscanf( g_szLine, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &rot[0], &rot[1], &rot[2] ) == 7)
+ {
+ // startframe is sanity check for having determined time
+ if (psource->startframe < 0)
+ {
+ MdlError( "Missing frame start(%d) : %s", g_iLinecount, g_szLine );
+ }
+
+ // scale if pertinent
+ scale_vertex( pos );
+ VectorCopy( pos, psource->rawanim[t][index].pos );
+ VectorCopy( rot, psource->rawanim[t][index].rot );
+
+ clip_rotations( rot ); // !!!
+ }
+ else if (sscanf( g_szLine, "%1023s %d", cmd, &index ))
+ {
+ // get time
+ if (strcmp( cmd, "time" ) == 0)
+ {
+ // again time IS an index
+ t = index;
+ if (psource->startframe == -1)
+ {
+ psource->startframe = t;
+ }
+ // sanity check time (little funny logic here, see previous IF)
+ if (t < psource->startframe)
+ {
+ MdlError( "Frame MdlError(%d) : %s", g_iLinecount, g_szLine );
+ }
+ // bump up endframe?
+ if (t > psource->endframe)
+ {
+ psource->endframe = t;
+ }
+ // make t into pure index
+ t -= psource->startframe;
+
+ // check for memory allocation
+ if (psource->rawanim[t] == NULL)
+ {
+ // Allocate 1 frame of full bonecount
+ psource->rawanim[t] = (s_bone_t *)kalloc( 1, size );
+
+ // duplicate previous frames keys?? preventative sanity?
+ if (t > 0 && psource->rawanim[t-1])
+ {
+ for (int j = 0; j < psource->numbones; j++)
+ {
+ VectorCopy( psource->rawanim[t-1][j].pos, psource->rawanim[t][j].pos );
+ VectorCopy( psource->rawanim[t-1][j].rot, psource->rawanim[t][j].rot );
+ }
+ }
+ }
+ else
+ {
+ // MdlError( "%s has duplicated frame %d\n", psource->filename, t );
+ }
+ }
+ else if (strcmp( cmd, "end") == 0)
+ {
+ psource->numframes = psource->endframe - psource->startframe + 1;
+
+ for (t = 0; t < psource->numframes; t++)
+ {
+ if (psource->rawanim[t] == NULL)
+ {
+ MdlError( "%s is missing frame %d\n", psource->filename, t + psource->startframe );
+ }
+ }
+
+ Build_Reference( psource );
+ return;
+ }
+ else
+ {
+ MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
+ }
+ }
+ else
+ {
+ MdlError( "MdlError(%d) : %s", g_iLinecount, g_szLine );
+ }
+ }
+
+ MdlError( "unexpected EOF: %s\n", psource->filename );
+}
+
+int lookup_index( s_source_t *psource, int material, Vector& vertex, Vector& normal, Vector2D texcoord )
+{
+ int i;
+
+ for (i = 0; i < numvlist; i++)
+ {
+ if (v_listdata[i].m == material
+ && DotProduct( g_normal[i], normal ) > normal_blend
+ && VectorCompare( g_vertex[i], vertex )
+ && g_texcoord[i][0] == texcoord[0]
+ && g_texcoord[i][1] == texcoord[1])
+ {
+ v_listdata[i].lastref = numvlist;
+ return i;
+ }
+ }
+ if (i >= MAXSTUDIOVERTS) {
+ MdlError( "too many indices in source: \"%s\"\n", psource->filename);
+ }
+
+ VectorCopy( vertex, g_vertex[i] );
+ VectorCopy( normal, g_normal[i] );
+ Vector2Copy( texcoord, g_texcoord[i] );
+
+ v_listdata[i].v = i;
+ v_listdata[i].m = material;
+ v_listdata[i].n = i;
+ v_listdata[i].t = i;
+
+ v_listdata[i].firstref = numvlist;
+ v_listdata[i].lastref = numvlist;
+
+ numvlist = i + 1;
+ return i;
+}
+
+
+void ParseFaceData( s_source_t *psource, int material, s_face_t *pFace )
+{
+ int index[3];
+ int i, j;
+ Vector p;
+ Vector normal;
+ Vector2D t;
+ int iCount, bones[MAXSTUDIOSRCBONES];
+ float weights[MAXSTUDIOSRCBONES];
+ int bone;
+
+ for (j = 0; j < 3; j++)
+ {
+ memset( g_szLine, 0, sizeof( g_szLine ) );
+
+ if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) == NULL)
+ {
+ MdlError("%s: error on g_szLine %d: %s", g_szFilename, g_iLinecount, g_szLine );
+ }
+
+ iCount = 0;
+
+ g_iLinecount++;
+ i = sscanf( g_szLine, "%d %f %f %f %f %f %f %f %f %d %d %f %d %f %d %f %d %f",
+ &bone,
+ &p[0], &p[1], &p[2],
+ &normal[0], &normal[1], &normal[2],
+ &t[0], &t[1],
+ &iCount,
+ &bones[0], &weights[0], &bones[1], &weights[1], &bones[2], &weights[2], &bones[3], &weights[3] );
+
+ if (i < 9)
+ continue;
+
+ if (bone < 0 || bone >= psource->numbones)
+ {
+ MdlError("bogus bone index\n%d %s :\n%s", g_iLinecount, g_szFilename, g_szLine );
+ }
+
+ //Scale face pos
+ scale_vertex( p );
+
+ // continue parsing more bones.
+ // FIXME: don't we have a built in parser that'll do this?
+ if (iCount > 4)
+ {
+ int k;
+ int ctr = 0;
+ char *token;
+ for (k = 0; k < 18; k++)
+ {
+ while (g_szLine[ctr] == ' ')
+ {
+ ctr++;
+ }
+ token = strtok( &g_szLine[ctr], " " );
+ ctr += strlen( token ) + 1;
+ }
+ for (k = 4; k < iCount && k < MAXSTUDIOSRCBONES; k++)
+ {
+ while (g_szLine[ctr] == ' ')
+ {
+ ctr++;
+ }
+ token = strtok( &g_szLine[ctr], " " );
+ ctr += strlen( token ) + 1;
+
+ bones[k] = atoi(token);
+
+ token = strtok( &g_szLine[ctr], " " );
+ ctr += strlen( token ) + 1;
+
+ weights[k] = atof(token);
+ }
+ // vprint( 0, "%d ", iCount );
+
+ //vprint( 0, "\n");
+ //exit(1);
+ }
+
+ // adjust_vertex( p );
+ // scale_vertex( p );
+
+ // move vertex position to object space.
+ // VectorSubtract( p, psource->bonefixup[bone].worldorg, tmp );
+ // VectorTransform(tmp, psource->bonefixup[bone].im, p );
+
+ // move normal to object space.
+ // VectorCopy( normal, tmp );
+ // VectorTransform(tmp, psource->bonefixup[bone].im, normal );
+ // VectorNormalize( normal );
+
+ // invert v
+ t[1] = 1.0 - t[1];
+
+ index[j] = lookup_index( psource, material, p, normal, t );
+
+ if (i == 9 || iCount == 0)
+ {
+ g_bone[index[j]].numbones = 1;
+ g_bone[index[j]].bone[0] = bone;
+ g_bone[index[j]].weight[0] = 1.0;
+ }
+ else
+ {
+ iCount = SortAndBalanceBones( iCount, MAXSTUDIOBONEWEIGHTS, bones, weights );
+
+ g_bone[index[j]].numbones = iCount;
+ for (i = 0; i < iCount; i++)
+ {
+ g_bone[index[j]].bone[i] = bones[i];
+ g_bone[index[j]].weight[i] = weights[i];
+ }
+ }
+ }
+
+ // pFace->material = material; // BUG
+ pFace->a = index[0];
+ pFace->b = index[1];
+ pFace->c = index[2];
+ Assert( ((pFace->a & 0xF0000000) == 0) && ((pFace->b & 0xF0000000) == 0) &&
+ ((pFace->c & 0xF0000000) == 0) );
+
+ if (flip_triangles)
+ {
+ j = pFace->b; pFace->b = pFace->c; pFace->c = j;
+ }
+}
+
+int use_texture_as_material( int textureindex )
+{
+ if (g_texture[textureindex].material == -1)
+ {
+ // vprint( 0, "%d %d %s\n", textureindex, g_nummaterials, g_texture[textureindex].name );
+ g_material[g_nummaterials] = textureindex;
+ g_texture[textureindex].material = g_nummaterials++;
+ }
+
+ return g_texture[textureindex].material;
+}
+
+int material_to_texture( int material )
+{
+ int i;
+ for (i = 0; i < g_numtextures; i++)
+ {
+ if (g_texture[i].material == material)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int lookup_texture( char *texturename, int maxlen )
+{
+ int i;
+
+ Q_StripExtension( texturename, texturename, maxlen );
+
+ for (i = 0; i < g_numtextures; i++)
+ {
+ if (stricmp( g_texture[i].name, texturename ) == 0)
+ {
+ return i;
+ }
+ }
+
+ if (i >= MAXSTUDIOSKINS)
+ MdlError("Too many materials used, max %d\n", ( int )MAXSTUDIOSKINS );
+
+// vprint( 0, "texture %d = %s\n", i, texturename );
+ V_strcpy_safe( g_texture[i].name, texturename );
+
+ g_texture[i].material = -1;
+ /*
+ if (stristr( texturename, "chrome" ) != NULL) {
+ texture[i].flags = STUDIO_NF_FLATSHADE | STUDIO_NF_CHROME;
+ }
+ else {
+ texture[i].flags = 0;
+ }
+ */
+ g_numtextures++;
+ return i;
+}
+
+int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] )
+{
+ int i;
+
+ // collapse duplicate bone weights
+ for (i = 0; i < iCount-1; i++)
+ {
+ int j;
+ for (j = i + 1; j < iCount; j++)
+ {
+ if (bones[i] == bones[j])
+ {
+ weights[i] += weights[j];
+ weights[j] = 0.0;
+ }
+ }
+ }
+
+ // do sleazy bubble sort
+ int bShouldSort;
+ do {
+ bShouldSort = false;
+ for (i = 0; i < iCount-1; i++)
+ {
+ if (weights[i+1] > weights[i])
+ {
+ int j = bones[i+1]; bones[i+1] = bones[i]; bones[i] = j;
+ float w = weights[i+1]; weights[i+1] = weights[i]; weights[i] = w;
+ bShouldSort = true;
+ }
+ }
+ } while (bShouldSort);
+
+ // throw away all weights less than 1/20th
+ while (iCount > 1 && weights[iCount-1] < 0.05)
+ {
+ iCount--;
+ }
+
+ // clip to the top iMaxCount bones
+ if (iCount > iMaxCount)
+ {
+ iCount = iMaxCount;
+ }
+
+ float t = 0;
+ for (i = 0; i < iCount; i++)
+ {
+ t += weights[i];
+ }
+
+ if (t <= 0.0)
+ {
+ // missing weights?, go ahead and evenly share?
+ // FIXME: shouldn't this error out?
+ t = 1.0 / iCount;
+
+ for (i = 0; i < iCount; i++)
+ {
+ weights[i] = t;
+ }
+ }
+ else
+ {
+ // scale to sum to 1.0
+ t = 1.0 / t;
+
+ for (i = 0; i < iCount; i++)
+ {
+ weights[i] = weights[i] * t;
+ }
+ }
+
+ return iCount;
+}
+
+int vlistCompare( const void *elem1, const void *elem2 )
+{
+ v_unify_t *u1 = &v_listdata[*(int *)elem1];
+ v_unify_t *u2 = &v_listdata[*(int *)elem2];
+
+ // sort by material
+ if (u1->m < u2->m)
+ return -1;
+ if (u1->m > u2->m)
+ return 1;
+
+ // sort by last used
+ if (u1->lastref < u2->lastref)
+ return -1;
+ if (u1->lastref > u2->lastref)
+ return 1;
+
+ return 0;
+}
+
+int faceCompare( const void *elem1, const void *elem2 )
+{
+ int i1 = *(int *)elem1;
+ int i2 = *(int *)elem2;
+
+ // sort by material
+ if (g_face[i1].material < g_face[i2].material)
+ return -1;
+ if (g_face[i1].material > g_face[i2].material)
+ return 1;
+
+ // sort by original usage
+ if (i1 < i2)
+ return -1;
+ if (i1 > i2)
+ return 1;
+
+ return 0;
+}
+
+#define SMALL_FLOAT 1e-12
+
+// NOTE: This routine was taken (and modified) from NVidia's BlinnReflection demo
+// Creates basis vectors, based on a vertex and index list.
+// See the NVidia white paper 'GDC2K PerPixel Lighting' for a description
+// of how this computation works
+static void CalcTriangleTangentSpace( s_source_t *pSrc, int v1, int v2, int v3,
+ Vector &sVect, Vector &tVect )
+{
+/*
+ static bool firstTime = true;
+ static FILE *fp = NULL;
+ if( firstTime )
+ {
+ firstTime = false;
+ fp = fopen( "crap.out", "w" );
+ }
+*/
+
+ /* Compute the partial derivatives of X, Y, and Z with respect to S and T. */
+ Vector2D t0( pSrc->texcoord[v1][0], pSrc->texcoord[v1][1] );
+ Vector2D t1( pSrc->texcoord[v2][0], pSrc->texcoord[v2][1] );
+ Vector2D t2( pSrc->texcoord[v3][0], pSrc->texcoord[v3][1] );
+ Vector p0( pSrc->vertex[v1][0], pSrc->vertex[v1][1], pSrc->vertex[v1][2] );
+ Vector p1( pSrc->vertex[v2][0], pSrc->vertex[v2][1], pSrc->vertex[v2][2] );
+ Vector p2( pSrc->vertex[v3][0], pSrc->vertex[v3][1], pSrc->vertex[v3][2] );
+
+ sVect.Init( 0.0f, 0.0f, 0.0f );
+ tVect.Init( 0.0f, 0.0f, 0.0f );
+
+ // x, s, t
+ Vector edge01 = Vector( p1.x - p0.x, t1.x - t0.x, t1.y - t0.y );
+ Vector edge02 = Vector( p2.x - p0.x, t2.x - t0.x, t2.y - t0.y );
+
+ Vector cross;
+ CrossProduct( edge01, edge02, cross );
+ if( fabs( cross.x ) > SMALL_FLOAT )
+ {
+ sVect.x += -cross.y / cross.x;
+ tVect.x += -cross.z / cross.x;
+ }
+
+ // y, s, t
+ edge01 = Vector( p1.y - p0.y, t1.x - t0.x, t1.y - t0.y );
+ edge02 = Vector( p2.y - p0.y, t2.x - t0.x, t2.y - t0.y );
+
+ CrossProduct( edge01, edge02, cross );
+ if( fabs( cross.x ) > SMALL_FLOAT )
+ {
+ sVect.y += -cross.y / cross.x;
+ tVect.y += -cross.z / cross.x;
+ }
+
+ // z, s, t
+ edge01 = Vector( p1.z - p0.z, t1.x - t0.x, t1.y - t0.y );
+ edge02 = Vector( p2.z - p0.z, t2.x - t0.x, t2.y - t0.y );
+
+ CrossProduct( edge01, edge02, cross );
+ if( fabs( cross.x ) > SMALL_FLOAT )
+ {
+ sVect.z += -cross.y / cross.x;
+ tVect.z += -cross.z / cross.x;
+ }
+
+ // Normalize sVect and tVect
+ VectorNormalize( sVect );
+ VectorNormalize( tVect );
+
+/*
+ // Calculate flat normal
+ Vector flatNormal;
+ edge01 = p1 - p0;
+ edge02 = p2 - p0;
+ CrossProduct( edge02, edge01, flatNormal );
+ VectorNormalize( flatNormal );
+
+ // Get the average position
+ Vector avgPos = ( p0 + p1 + p2 ) / 3.0f;
+
+ // Draw the svect
+ Vector endS = avgPos + sVect * .2f;
+ fvprint( 0, fp, "2\n" );
+ fvprint( 0, fp, "%f %f %f 1.0 0.0 0.0\n", endS[0], endS[1], endS[2] );
+ fvprint( 0, fp, "%f %f %f 1.0 0.0 0.0\n", avgPos[0], avgPos[1], avgPos[2] );
+
+ // Draw the tvect
+ Vector endT = avgPos + tVect * .2f;
+ fvprint( 0, fp, "2\n" );
+ fvprint( 0, fp, "%f %f %f 0.0 1.0 0.0\n", endT[0], endT[1], endT[2] );
+ fvprint( 0, fp, "%f %f %f 0.0 1.0 0.0\n", avgPos[0], avgPos[1], avgPos[2] );
+
+ // Draw the normal
+ Vector endN = avgPos + flatNormal * .2f;
+ fvprint( 0, fp, "2\n" );
+ fvprint( 0, fp, "%f %f %f 0.0 0.0 1.0\n", endN[0], endN[1], endN[2] );
+ fvprint( 0, fp, "%f %f %f 0.0 0.0 1.0\n", avgPos[0], avgPos[1], avgPos[2] );
+
+ // Draw the wireframe of the triangle in white.
+ fvprint( 0, fp, "2\n" );
+ fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p0[0], p0[1], p0[2] );
+ fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p1[0], p1[1], p1[2] );
+ fvprint( 0, fp, "2\n" );
+ fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p1[0], p1[1], p1[2] );
+ fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p2[0], p2[1], p2[2] );
+ fvprint( 0, fp, "2\n" );
+ fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p2[0], p2[1], p2[2] );
+ fvprint( 0, fp, "%f %f %f 1.0 1.0 1.0\n", p0[0], p0[1], p0[2] );
+
+ // Draw a slightly shrunken version of the geometry to hide surfaces
+ Vector tmp0 = p0 - flatNormal * .1f;
+ Vector tmp1 = p1 - flatNormal * .1f;
+ Vector tmp2 = p2 - flatNormal * .1f;
+ fvprint( 0, fp, "3\n" );
+ fvprint( 0, fp, "%f %f %f 0.1 0.1 0.1\n", tmp0[0], tmp0[1], tmp0[2] );
+ fvprint( 0, fp, "%f %f %f 0.1 0.1 0.1\n", tmp1[0], tmp1[1], tmp1[2] );
+ fvprint( 0, fp, "%f %f %f 0.1 0.1 0.1\n", tmp2[0], tmp2[1], tmp2[2] );
+
+ fflush( fp );
+*/
+}
+
+typedef CUtlVector<int> CIntVector;
+
+void CalcModelTangentSpaces( s_source_t *pSrc )
+{
+ // Build a map from vertex to a list of triangles that share the vert.
+ int meshID;
+ for( meshID = 0; meshID < pSrc->nummeshes; meshID++ )
+ {
+ s_mesh_t *pMesh = &pSrc->mesh[pSrc->meshindex[meshID]];
+ CUtlVector<CIntVector> vertToTriMap;
+ vertToTriMap.AddMultipleToTail( pMesh->numvertices );
+ int triID;
+ for( triID = 0; triID < pMesh->numfaces; triID++ )
+ {
+ s_face_t *pFace = &pSrc->face[triID + pMesh->faceoffset];
+ vertToTriMap[pFace->a].AddToTail( triID );
+ vertToTriMap[pFace->b].AddToTail( triID );
+ vertToTriMap[pFace->c].AddToTail( triID );
+ }
+
+ // Calculate the tangent space for each triangle.
+ CUtlVector<Vector> triSVect;
+ CUtlVector<Vector> triTVect;
+ triSVect.AddMultipleToTail( pMesh->numfaces );
+ triTVect.AddMultipleToTail( pMesh->numfaces );
+ for( triID = 0; triID < pMesh->numfaces; triID++ )
+ {
+ s_face_t *pFace = &pSrc->face[triID + pMesh->faceoffset];
+ CalcTriangleTangentSpace( pSrc,
+ pMesh->vertexoffset + pFace->a,
+ pMesh->vertexoffset + pFace->b,
+ pMesh->vertexoffset + pFace->c,
+ triSVect[triID], triTVect[triID] );
+ }
+
+ // calculate an average tangent space for each vertex.
+ int vertID;
+ for( vertID = 0; vertID < pMesh->numvertices; vertID++ )
+ {
+ const Vector &normal = pSrc->normal[vertID+pMesh->vertexoffset];
+ Vector4D &finalSVect = pSrc->tangentS[vertID+pMesh->vertexoffset];
+ Vector sVect, tVect;
+
+ sVect.Init( 0.0f, 0.0f, 0.0f );
+ tVect.Init( 0.0f, 0.0f, 0.0f );
+ for( triID = 0; triID < vertToTriMap[vertID].Size(); triID++ )
+ {
+ sVect += triSVect[vertToTriMap[vertID][triID]];
+ tVect += triTVect[vertToTriMap[vertID][triID]];
+ }
+
+ // In the case of zbrush, everything needs to be treated as smooth.
+ if( g_bZBrush )
+ {
+ int vertID2;
+ Vector vertPos1( pSrc->vertex[vertID][0], pSrc->vertex[vertID][1], pSrc->vertex[vertID][2] );
+ for( vertID2 = 0; vertID2 < pMesh->numvertices; vertID2++ )
+ {
+ if( vertID2 == vertID )
+ {
+ continue;
+ }
+ Vector vertPos2( pSrc->vertex[vertID2][0], pSrc->vertex[vertID2][1], pSrc->vertex[vertID2][2] );
+ if( vertPos1 == vertPos2 )
+ {
+ int triID2;
+ for( triID2 = 0; triID2 < vertToTriMap[vertID2].Size(); triID2++ )
+ {
+ sVect += triSVect[vertToTriMap[vertID2][triID2]];
+ tVect += triTVect[vertToTriMap[vertID2][triID2]];
+ }
+ }
+ }
+ }
+
+ // make an orthonormal system.
+ // need to check if we are left or right handed.
+ Vector tmpVect;
+ CrossProduct( sVect, tVect, tmpVect );
+ bool leftHanded = DotProduct( tmpVect, normal ) < 0.0f;
+ if( !leftHanded )
+ {
+ CrossProduct( normal, sVect, tVect );
+ CrossProduct( tVect, normal, sVect );
+ VectorNormalize( sVect );
+ VectorNormalize( tVect );
+ finalSVect[0] = sVect[0];
+ finalSVect[1] = sVect[1];
+ finalSVect[2] = sVect[2];
+ finalSVect[3] = 1.0f;
+ }
+ else
+ {
+ CrossProduct( sVect, normal, tVect );
+ CrossProduct( normal, tVect, sVect );
+ VectorNormalize( sVect );
+ VectorNormalize( tVect );
+ finalSVect[0] = sVect[0];
+ finalSVect[1] = sVect[1];
+ finalSVect[2] = sVect[2];
+ finalSVect[3] = -1.0f;
+ }
+ }
+ }
+}
+
+void BuildIndividualMeshes( s_source_t *psource )
+{
+ int i, j, k;
+
+ // sort new vertices by materials, last used
+ static int v_listsort[MAXSTUDIOVERTS]; // map desired order to vlist entry
+ static int v_ilistsort[MAXSTUDIOVERTS]; // map vlist entry to desired order
+
+ for (i = 0; i < numvlist; i++)
+ {
+ v_listsort[i] = i;
+ }
+ qsort( v_listsort, numvlist, sizeof( int ), vlistCompare );
+ for (i = 0; i < numvlist; i++)
+ {
+ v_ilistsort[v_listsort[i]] = i;
+ }
+
+
+ // allocate memory
+ psource->numvertices = numvlist;
+ psource->localBoneweight = (s_boneweight_t *)kalloc( psource->numvertices, sizeof( s_boneweight_t ) );
+ psource->globalBoneweight = NULL;
+ psource->vertexInfo = (s_vertexinfo_t *)kalloc( psource->numvertices, sizeof( s_vertexinfo_t ) );
+ psource->vertex = new Vector[psource->numvertices];
+ psource->normal = new Vector[psource->numvertices];
+ psource->tangentS = new Vector4D[psource->numvertices];
+ psource->texcoord = (Vector2D *)kalloc( psource->numvertices, sizeof( Vector2D ) );
+
+ // create arrays of unique vertexes, normals, texcoords.
+ for (i = 0; i < psource->numvertices; i++)
+ {
+ j = v_listsort[i];
+
+ VectorCopy( g_vertex[v_listdata[j].v], psource->vertex[i] );
+ VectorCopy( g_normal[v_listdata[j].n], psource->normal[i] );
+ Vector2Copy( g_texcoord[v_listdata[j].t], psource->texcoord[i] );
+
+ psource->localBoneweight[i].numbones = g_bone[v_listdata[j].v].numbones;
+ int k;
+ for( k = 0; k < MAXSTUDIOBONEWEIGHTS; k++ )
+ {
+ psource->localBoneweight[i].bone[k] = g_bone[v_listdata[j].v].bone[k];
+ psource->localBoneweight[i].weight[k] = g_bone[v_listdata[j].v].weight[k];
+ }
+
+ // store a bunch of other info
+ psource->vertexInfo[i].material = v_listdata[j].m;
+
+ psource->vertexInfo[i].firstref = v_listdata[j].firstref;
+ psource->vertexInfo[i].lastref = v_listdata[j].lastref;
+ // vprint( 0, "%4d : %2d : %6.2f %6.2f %6.2f\n", i, psource->boneweight[i].bone[0], psource->vertex[i][0], psource->vertex[i][1], psource->vertex[i][2] );
+ }
+
+ // sort faces by materials, last used.
+ static int facesort[MAXSTUDIOTRIANGLES]; // map desired order to src_face entry
+ static int ifacesort[MAXSTUDIOTRIANGLES]; // map src_face entry to desired order
+
+ for (i = 0; i < g_numfaces; i++)
+ {
+ facesort[i] = i;
+ }
+ qsort( facesort, g_numfaces, sizeof( int ), faceCompare );
+ for (i = 0; i < g_numfaces; i++)
+ {
+ ifacesort[facesort[i]] = i;
+ }
+
+ psource->numfaces = g_numfaces;
+ // find first occurance for each material
+ for (k = 0; k < MAXSTUDIOSKINS; k++)
+ {
+ psource->mesh[k].numvertices = 0;
+ psource->mesh[k].vertexoffset = psource->numvertices;
+
+ psource->mesh[k].numfaces = 0;
+ psource->mesh[k].faceoffset = g_numfaces;
+ }
+
+ // find first and count of indices per material
+ for (i = 0; i < psource->numvertices; i++)
+ {
+ k = psource->vertexInfo[i].material;
+ psource->mesh[k].numvertices++;
+ if (psource->mesh[k].vertexoffset > i)
+ psource->mesh[k].vertexoffset = i;
+ }
+
+ // find first and count of faces per material
+ for (i = 0; i < psource->numfaces; i++)
+ {
+ k = g_face[facesort[i]].material;
+
+ psource->mesh[k].numfaces++;
+ if (psource->mesh[k].faceoffset > i)
+ psource->mesh[k].faceoffset = i;
+ }
+
+ /*
+ for (k = 0; k < MAXSTUDIOSKINS; k++)
+ {
+ vprint( 0, "%d : %d:%d %d:%d\n", k, psource->mesh[k].numvertices, psource->mesh[k].vertexoffset, psource->mesh[k].numfaces, psource->mesh[k].faceoffset );
+ }
+ */
+
+ // create remapped faces
+ psource->face = (s_face_t *)kalloc( psource->numfaces, sizeof( s_face_t ));
+ for (k = 0; k < MAXSTUDIOSKINS; k++)
+ {
+ if (psource->mesh[k].numfaces)
+ {
+ psource->meshindex[psource->nummeshes] = k;
+
+ for (i = psource->mesh[k].faceoffset; i < psource->mesh[k].numfaces + psource->mesh[k].faceoffset; i++)
+ {
+ j = facesort[i];
+
+ psource->face[i].a = v_ilistsort[g_src_uface[j].a] - psource->mesh[k].vertexoffset;
+ psource->face[i].b = v_ilistsort[g_src_uface[j].b] - psource->mesh[k].vertexoffset;
+ psource->face[i].c = v_ilistsort[g_src_uface[j].c] - psource->mesh[k].vertexoffset;
+ Assert( ((psource->face[i].a & 0xF0000000) == 0) && ((psource->face[i].b & 0xF0000000) == 0) &&
+ ((psource->face[i].c & 0xF0000000) == 0) );
+ // vprint( 0, "%3d : %4d %4d %4d\n", i, psource->face[i].a, psource->face[i].b, psource->face[i].c );
+ }
+
+ psource->nummeshes++;
+ }
+ }
+
+ CalcModelTangentSpaces( psource );
+}
+
+void Grab_Triangles( s_source_t *psource )
+{
+ int i;
+ Vector vmin, vmax;
+
+ vmin[0] = vmin[1] = vmin[2] = 99999;
+ vmax[0] = vmax[1] = vmax[2] = -99999;
+
+ g_numfaces = 0;
+ numvlist = 0;
+
+ //
+ // load the base triangles
+ //
+ int texture;
+ int material;
+ char texturename[64];
+
+ while (1)
+ {
+ if (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) == NULL)
+ break;
+
+ g_iLinecount++;
+
+ // check for end
+ if (IsEnd( g_szLine ))
+ break;
+
+ // Look for extra junk that we may want to avoid...
+ int nLineLength = strlen( g_szLine );
+ if (nLineLength >= 64)
+ {
+ MdlWarning("Unexpected data at line %d, (need a texture name) ignoring...\n", g_iLinecount );
+ continue;
+ }
+
+ // strip off trailing smag
+ V_strcpy_safe( texturename, g_szLine );
+ for (i = strlen( texturename ) - 1; i >= 0 && ! isgraph( texturename[i] ); i--)
+ {
+ }
+ texturename[i + 1] = '\0';
+
+ // funky texture overrides
+ for (i = 0; i < numrep; i++)
+ {
+ if (sourcetexture[i][0] == '\0')
+ {
+ strcpy( texturename, defaulttexture[i] );
+ break;
+ }
+ if (stricmp( texturename, sourcetexture[i]) == 0)
+ {
+ strcpy( texturename, defaulttexture[i] );
+ break;
+ }
+ }
+
+ if (texturename[0] == '\0')
+ {
+ // weird source problem, skip them
+ fgets( g_szLine, sizeof( g_szLine ), g_fpInput );
+ fgets( g_szLine, sizeof( g_szLine ), g_fpInput );
+ fgets( g_szLine, sizeof( g_szLine ), g_fpInput );
+ g_iLinecount += 3;
+ continue;
+ }
+
+ if (stricmp( texturename, "null.bmp") == 0 || stricmp( texturename, "null.tga") == 0)
+ {
+ // skip all faces with the null texture on them.
+ fgets( g_szLine, sizeof( g_szLine ), g_fpInput );
+ fgets( g_szLine, sizeof( g_szLine ), g_fpInput );
+ fgets( g_szLine, sizeof( g_szLine ), g_fpInput );
+ g_iLinecount += 3;
+ continue;
+ }
+
+ texture = lookup_texture( texturename, sizeof( texturename ) );
+ psource->texmap[texture] = texture; // hack, make it 1:1
+ material = use_texture_as_material( texture );
+
+ s_face_t f;
+ ParseFaceData( psource, material, &f );
+
+ g_src_uface[g_numfaces] = f;
+ g_face[g_numfaces].material = material;
+ g_numfaces++;
+ }
+
+ BuildIndividualMeshes( psource );
+}
+
+//--------------------------------------------------------------------
+// Load a SMD file
+//--------------------------------------------------------------------
+int Load_SMD ( s_source_t *psource )
+{
+ char cmd[1024];
+ int option;
+
+ // Open file
+ if (!OpenGlobalFile( psource->filename ))
+ return 0;
+
+ // verbose
+ if( !g_quiet )
+ {
+ printf ("SMD MODEL %s\n", psource->filename);
+ }
+
+ //March through lines
+ g_iLinecount = 0;
+ while (fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
+ {
+ g_iLinecount++;
+ int numRead = sscanf( g_szLine, "%s %d", cmd, &option );
+
+ // Blank line
+ if ((numRead == EOF) || (numRead == 0))
+ continue;
+
+ if (strcmp( cmd, "version" ) == 0)
+ {
+ if (option != 1)
+ {
+ MdlError("bad version\n");
+ }
+ }
+ // Get hierarchy?
+ else if (strcmp( cmd, "nodes" ) == 0)
+ {
+ psource->numbones = Grab_Nodes( psource->localBone );
+ }
+ // Get animation??
+ else if (strcmp( cmd, "skeleton" ) == 0)
+ {
+ Grab_Animation( psource );
+ }
+ // Geo?
+ else if (strcmp( cmd, "triangles" ) == 0)
+ {
+ Grab_Triangles( psource );
+ }
+ // Geo animation
+ else if (strcmp( cmd, "vertexanimation" ) == 0)
+ {
+ Grab_Vertexanimation( psource );
+ }
+ else
+ {
+ MdlWarning("unknown studio command\n" );
+ }
+ }
+ fclose( g_fpInput );
+
+ is_v1support = true;
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Checks to see if the model source was already loaded
+//-----------------------------------------------------------------------------
+static s_source_t *FindCachedSource( char const* name, char const* xext )
+{
+ int i;
+
+ if( xext[0] )
+ {
+ // we know what extension is necessary. . look for it.
+ sprintf (g_szFilename, "%s%s.%s", cddir[numdirs], name, xext );
+ for (i = 0; i < g_numsources; i++)
+ {
+ if (stricmp( g_szFilename, g_source[i]->filename ) == 0)
+ return g_source[i];
+ }
+ }
+ else
+ {
+ // we don't know what extension to use, so look for all of 'em.
+ sprintf (g_szFilename, "%s%s.vrm", cddir[numdirs], name );
+ for (i = 0; i < g_numsources; i++)
+ {
+ if (stricmp( g_szFilename, g_source[i]->filename ) == 0)
+ return g_source[i];
+ }
+ sprintf (g_szFilename, "%s%s.smd", cddir[numdirs], name );
+ for (i = 0; i < g_numsources; i++)
+ {
+ if (stricmp( g_szFilename, g_source[i]->filename ) == 0)
+ return g_source[i];
+ }
+ /*
+ sprintf (g_szFilename, "%s%s.vta", cddir[numdirs], name );
+ for (i = 0; i < g_numsources; i++)
+ {
+ if (stricmp( g_szFilename, g_source[i]->filename ) == 0)
+ return g_source[i];
+ }
+ */
+ }
+
+ // Not found
+ return 0;
+}
+
+static void FlipFacing( s_source_t *pSrc )
+{
+ unsigned short tmp;
+
+ int i, j;
+ for( i = 0; i < pSrc->nummeshes; i++ )
+ {
+ s_mesh_t *pMesh = &pSrc->mesh[i];
+ for( j = 0; j < pMesh->numfaces; j++ )
+ {
+ s_face_t &f = pSrc->face[pMesh->faceoffset + j];
+ tmp = f.b; f.b = f.c; f.c = tmp;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Loads an animation source
+//-----------------------------------------------------------------------------
+
+s_source_t *Load_Source( char const *name, const char *ext, bool reverse, bool isActiveModel )
+{
+ // Sanity check number of source files
+ if ( g_numsources >= MAXSTUDIOSEQUENCES )
+ MdlError( "Load_Source( %s ) - overflowed g_numsources.", name );
+
+ // Sanity check file and init
+ Assert(name);
+ int namelen = strlen(name) + 1;
+ char* pTempName = (char*)_alloca( namelen );
+ char xext[32];
+ int result = false;
+
+ // Local copy of filename
+ strcpy( pTempName, name );
+
+ // Sanity check file extension?
+ Q_ExtractFileExtension( pTempName, xext, sizeof( xext ) );
+ if (xext[0] == '\0')
+ {
+ V_strcpy_safe( xext, ext );
+ }
+ else
+ {
+ Q_StripExtension( pTempName, pTempName, namelen );
+ }
+
+ // Cached source, ie: already loaded model, legacy
+ // s_source_t* pSource = FindCachedSource( pTempName, xext );
+ // if (pSource)
+ // {
+ // if (isActiveModel)
+ // pSource->isActiveModel = true;
+ // return pSource;
+ // }
+
+ // allocate space and whatnot
+ g_source[g_numsources] = (s_source_t *)kalloc( 1, sizeof( s_source_t ) );
+ V_strcpy_safe( g_source[g_numsources]->filename, g_szFilename );
+
+ // legacy stuff
+ if (isActiveModel)
+ {
+ g_source[g_numsources]->isActiveModel = true;
+ }
+
+ // more ext sanity check
+ if ( ( !result && xext[0] == '\0' ) || stricmp( xext, "smd" ) == 0)
+ {
+ Q_snprintf( g_szFilename, sizeof(g_szFilename), "%s%s.smd", cddir[numdirs], pTempName );
+ V_strcpy_safe( g_source[g_numsources]->filename, g_szFilename );
+
+ // Import part, load smd file
+ result = Load_SMD( g_source[g_numsources] );
+ }
+
+ /*
+ if ( ( !result && xext[0] == '\0' ) || stricmp( xext, "dmx" ) == 0)
+ {
+ Q_snprintf( g_szFilename, sizeof(g_szFilename), "%s%s.dmx", cddir[numdirs], pTempName );
+ V_strcpy_safe( g_source[g_numsources]->filename, g_szFilename );
+
+ // Import part, load smd file
+ result = Load_DMX( g_source[g_numsources] );
+ }
+ */
+
+ // Oops
+ if ( !result)
+ {
+ MdlError( "could not load file '%s'\n", g_source[g_numsources]->filename );
+ }
+
+ // bump up number of sources
+ g_numsources++;
+ if( reverse )
+ {
+ FlipFacing( g_source[g_numsources-1] );
+ }
+ return g_source[g_numsources-1];
+}
+
+void SaveNodes( s_source_t *source, CUtlBuffer& buf )
+{
+ if ( source->numbones <= 0 )
+ return;
+
+ buf.Printf( "nodes\n" );
+
+ for ( int i = 0; i < source->numbones; ++i )
+ {
+ s_node_t *bone = &source->localBone[ i ];
+
+ buf.Printf( "%d \"%s\" %d\n", i, bone->name, bone->parent );
+ }
+
+ buf.Printf( "end\n" );
+}
+
+// FIXME: since we don't us a .qc, we could have problems with scaling, etc.???
+void descale_vertex( Vector &org )
+{
+ float invscale = 1.0f / g_currentscale;
+
+ org[0] = org[0] * invscale;
+ org[1] = org[1] * invscale;
+ org[2] = org[2] * invscale;
+}
+
+void SaveAnimation( s_source_t *source, CUtlBuffer& buf )
+{
+ if ( source->numbones <= 0 )
+ return;
+
+ buf.Printf( "skeleton\n" );
+
+ for ( int frame = 0; frame < source->numframes; ++frame )
+ {
+ buf.Printf( "time %i\n", frame + source->startframe );
+
+ for ( int i = 0; i < source->numbones; ++i )
+ {
+ s_bone_t *prev = NULL;
+ if ( frame > 0 )
+ {
+ if ( source->rawanim[ frame - 1 ] )
+ {
+ prev = &source->rawanim[ frame - 1 ][ i ];
+ }
+ }
+
+ Vector pos = source->rawanim[ frame ][ i ].pos;
+ descale_vertex( pos );
+ RadianEuler rot = source->rawanim[ frame ][ i ].rot;
+
+// If this is enabled, then we delta this pos vs the prev frame and don't write out a sample if it's the same value...
+#if 0
+ if ( prev )
+ {
+ Vector ppos = source->rawanim[ frame -1 ][ i ].pos;
+ descale_vertex( pos );
+ RadianEuler prot = source->rawanim[ frame -1 ][ i ].rot;
+
+ // Only output it if there's a delta
+ if ( ( ppos != pos ) ||
+ Q_memcmp( &prot, &rot, sizeof( prot ) ) )
+ {
+ buf.Printf
+ ( "%d %f %f %f %f %f %f\n",
+ i, // bone index
+ pos[ 0 ],
+ pos[ 1 ],
+ pos[ 2 ],
+ rot[ 0 ],
+ rot[ 1 ],
+ rot[ 2 ]
+ );
+ }
+ }
+ else
+#endif
+ {
+ buf.Printf
+ ( "%d %f %f %f %f %f %f\n",
+ i, // bone index
+ pos[ 0 ],
+ pos[ 1 ],
+ pos[ 2 ],
+ rot[ 0 ],
+ rot[ 1 ],
+ rot[ 2 ]
+ );
+ }
+ }
+ }
+
+ buf.Printf( "end\n" );
+}
+
+void Save_SMD( char const *filename, s_source_t *source )
+{
+ // Text buffer
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ buf.Printf( "version 1\n" );
+
+ SaveNodes( source, buf );
+ SaveAnimation( source, buf );
+
+ FileHandle_t fh = g_pFileSystem->Open( filename, "wb" );
+ if ( FILESYSTEM_INVALID_HANDLE != fh )
+ {
+ g_pFileSystem->Write( buf.Base(), buf.TellPut(), fh );
+ g_pFileSystem->Close( fh );
+ }
+}
+
+//--------------------------------------------------------------------
+// mikes right handed row based linear algebra
+//--------------------------------------------------------------------
+struct M_matrix4x4_t
+{
+ M_matrix4x4_t() {
+
+ m_flMatVal[0][0] = 1.0; m_flMatVal[0][1] = 0.0; m_flMatVal[0][2] = 0.0; m_flMatVal[0][3] = 0.0;
+ m_flMatVal[1][0] = 0.0; m_flMatVal[1][1] = 1.0; m_flMatVal[1][2] = 0.0; m_flMatVal[1][3] = 0.0;
+ m_flMatVal[2][0] = 0.0; m_flMatVal[2][1] = 0.0; m_flMatVal[2][2] = 1.0; m_flMatVal[2][3] = 0.0;
+ m_flMatVal[3][0] = 0.0; m_flMatVal[3][1] = 0.0; m_flMatVal[3][2] = 0.0; m_flMatVal[3][3] = 1.0;
+
+ }
+ // M_matrix3x4_t(
+ // float m00, float m01, float m02,
+ // float m10, float m11, float m12,
+ // float m20, float m21, float m22,
+ // float m30, float m31, float m32)
+ // {
+ // m_flMatVal[0][0] = m00; m_flMatVal[0][1] = m01; m_flMatVal[0][2] = m02;
+ // m_flMatVal[1][0] = m10; m_flMatVal[1][1] = m11; m_flMatVal[1][2] = m12;
+ // m_flMatVal[2][0] = m20; m_flMatVal[2][1] = m21; m_flMatVal[2][2] = m22;
+ // m_flMatVal[3][0] = m30; m_flMatVal[3][1] = m31; m_flMatVal[3][2] = m32;
+
+ // }
+
+ float *operator[]( int i ) { Assert(( i >= 0 ) && ( i < 4 )); return m_flMatVal[i]; }
+ const float *operator[]( int i ) const { Assert(( i >= 0 ) && ( i < 4 )); return m_flMatVal[i]; }
+ float *Base() { return &m_flMatVal[0][0]; }
+ const float *Base() const { return &m_flMatVal[0][0]; }
+
+ float m_flMatVal[4][4];
+};
+
+void M_MatrixAngles( const M_matrix4x4_t& matrix, RadianEuler &angles, Vector &position)
+{
+ float cX, sX, cY, sY, cZ, sZ;
+
+ sY = -matrix[0][2];
+ cY = sqrtf(1.0-(sY*sY));
+
+ if (cY != 0.0)
+ {
+ sX = matrix[1][2];
+ cX = matrix[2][2];
+ sZ = matrix[0][1];
+ cZ = matrix[0][0];
+ }
+ else
+ {
+ sX = -matrix[2][1];
+ cX = matrix[1][1];
+ sZ = 0.0;
+ cZ = 1.0;
+ }
+
+ angles[0] = atan2f( sX, cX );
+ angles[2] = atan2f( sZ, cZ );
+
+ sX = sinf(angles[0]);
+ cX = cosf(angles[0]);
+
+ if (sX > cX)
+ cY = matrix[1][2] / sX;
+ else
+ cY = matrix[2][2] / cX;
+
+ angles[1] = atan2f( sY, cY );
+
+
+ position.x = matrix[3][0];
+ position.y = matrix[3][1];
+ position.z = matrix[3][2];
+
+}
+
+// void M_MatrixAngles( const M_matrix4x4_t& matrix, RadianEuler &angles, Vector &position)
+// {
+
+ // float cX, sX, cY, sY, cZ, sZ;
+
+ // sY = matrix[2][0];
+ // cY = sqrtf(1.0-(sY*sY));
+
+ // if (cY != 0.0)
+ // {
+ // sX = -matrix[2][1];
+ // cX = matrix[2][2];
+ // sZ = -matrix[1][0];
+ // cZ = matrix[0][0];
+ // }
+ // else
+ // {
+ // sX = matrix[0][1];
+ // cX = matrix[1][1];
+ // sZ = 0.0;
+ // cZ = 1.0;
+ // }
+
+ // angles[0] = atan2f( sX, cX );
+ // angles[2] = atan2f( sZ, cZ );
+
+ // sX = sinf(angles[0]);
+ // cX = cosf(angles[0]);
+
+ // if (sX > cX)
+ // cY = -matrix[2][1] / sX;
+ // else
+ // cY = matrix[2][2] / cX;
+
+ // angles[1] = atan2f( sY, cY );
+
+ // angles[0] = angles[0];
+ // angles[1] = angles[1];
+ // angles[2] = angles[2];
+
+ // position.x = matrix[3][0];
+ // position.y = matrix[3][1];
+ // position.z = matrix[3][2];
+// }
+
+void M_MatrixCopy( const M_matrix4x4_t& in, M_matrix4x4_t& out )
+{
+ // Assert( s_bMathlibInitialized );
+ memcpy( out.Base(), in.Base(), sizeof( float ) * 4 * 4 );
+}
+void M_RotateZMatrix(float radian, M_matrix4x4_t &resultMatrix)
+{
+
+ resultMatrix[0][0] = cosf(radian);
+ resultMatrix[0][1] = sin(radian);
+ resultMatrix[0][2] = 0.0;
+ resultMatrix[1][0] =-sin(radian);
+ resultMatrix[1][1] = cos(radian);
+ resultMatrix[1][2] = 0.0;
+ resultMatrix[2][0] = 0.0;
+ resultMatrix[2][1] = 0.0;
+ resultMatrix[2][2] = 1.0;
+}
+
+// !!! THIS SHIT DOESN'T WORK!! WHY? HAS I EVER?
+void M_AngleAboutAxis(Vector &axis, float radianAngle, M_matrix4x4_t &result)
+{
+ float c = cosf(radianAngle);
+ float s = sinf(radianAngle);
+ float t = 1.0 - c;
+ // axis.normalize();
+
+ result[0][0] = t * axis[0] * axis[0] + c;
+ result[0][1] = t * axis[0] * axis[1] - s * axis[2];
+ result[0][2] = t * axis[0] * axis[2] + s * axis[1];
+ result[1][0] = t * axis[0] * axis[1] + s * axis[2];
+ result[1][1] = t * axis[1] * axis[1] + c;
+ result[1][2] = t * axis[1] * axis[2] - s * axis[0];
+ result[2][0] = t * axis[1] * axis[2] - s;
+ result[2][1] = t * axis[1] * axis[2] + s * axis[1];
+ result[2][2] = t * axis[2] * axis[2] + c * axis[0];
+
+}
+
+
+void M_MatrixInvert( const M_matrix4x4_t& in, M_matrix4x4_t& out )
+{
+ // Assert( s_bMathlibInitialized );
+ if ( &in == &out )
+ {
+ M_matrix4x4_t in2;
+ M_MatrixCopy( in, in2 );
+ M_MatrixInvert( in2, out );
+ return;
+ }
+ float tmp[3];
+
+ // I'm guessing this only works on a 3x4 orthonormal matrix
+ out[0][0] = in[0][0];
+ out[1][0] = in[0][1];
+ out[2][0] = in[0][2];
+
+ out[0][1] = in[1][0];
+ out[1][1] = in[1][1];
+ out[2][1] = in[1][2];
+
+ out[0][2] = in[2][0];
+ out[1][2] = in[2][1];
+ out[2][2] = in[2][2];
+
+ tmp[0] = in[3][0];
+ tmp[1] = in[3][1];
+ tmp[2] = in[3][2];
+
+ float v1[3], v2[3], v3[3];
+ v1[0] = out[0][0];
+ v1[1] = out[1][0];
+ v1[2] = out[2][0];
+ v2[0] = out[0][1];
+ v2[1] = out[1][1];
+ v2[2] = out[2][1];
+ v3[0] = out[0][2];
+ v3[1] = out[1][2];
+ v3[2] = out[2][2];
+
+ out[3][0] = -DotProduct( tmp, v1 );
+ out[3][1] = -DotProduct( tmp, v2 );
+ out[3][2] = -DotProduct( tmp, v3 );
+
+ // Trivial case
+ // if (IS_IDENTITY(matrix))
+ // return SbMatrix::identity();
+
+ // // Affine case...
+ // // SbMatrix affineAnswer;
+ // // if ( affine_inverse( SbMatrix(matrix), affineAnswer ) )
+ // // return affineAnswer;
+
+ // int index[4];
+ // float d, invmat[4][4], temp;
+ // SbMatrix inverse = *this;
+
+ // if(inverse.LUDecomposition(index, d)) {
+
+ // invmat[0][0] = 1.0;
+ // invmat[0][1] = 0.0;
+ // invmat[0][2] = 0.0;
+ // invmat[0][3] = 0.0;
+ // inverse.LUBackSubstitution(index, invmat[0]);
+ // invmat[1][0] = 0.0;
+ // invmat[1][1] = 1.0;
+ // invmat[1][2] = 0.0;
+ // invmat[1][3] = 0.0;
+ // inverse.LUBackSubstitution(index, invmat[1]);
+ // invmat[2][0] = 0.0;
+ // invmat[2][1] = 0.0;
+ // invmat[2][2] = 1.0;
+ // invmat[2][3] = 0.0;
+ // inverse.LUBackSubstitution(index, invmat[2]);
+ // invmat[3][0] = 0.0;
+ // invmat[3][1] = 0.0;
+ // invmat[3][2] = 0.0;
+ // invmat[3][3] = 1.0;
+ // inverse.LUBackSubstitution(index, invmat[3]);
+
+// #define SWAP(i,j) \
+ // temp = invmat[i][j]; \
+ // invmat[i][j] = invmat[j][i]; \
+ // invmat[j][i] = temp;
+
+ // SWAP(1,0);
+
+ // SWAP(2,0);
+ // SWAP(2,1);
+
+ // SWAP(3,0);
+ // SWAP(3,1);
+ // SWAP(3,2);
+// #undef SWAP
+ // }
+}
+
+/*
+================
+M_ConcatTransforms
+================
+*/
+void M_ConcatTransforms (const M_matrix4x4_t &in1, const M_matrix4x4_t &in2, M_matrix4x4_t &out)
+{
+
+ // Assert( s_bMathlibInitialized );
+ // if ( &in1 == &out )
+ // {
+ // matrix3x4_t in1b;
+ // MatrixCopy( in1, in1b );
+ // ConcatTransforms( in1b, in2, out );
+ // return;
+ // }
+ // if ( &in2 == &out )
+ // {
+ // matrix3x4_t in2b;
+ // MatrixCopy( in2, in2b );
+ // ConcatTransforms( in1, in2b, out );
+ // return;
+ // }
+
+#define MULT(i,j) (in1[i][0]*in2[0][j] + \
+ in1[i][1]*in2[1][j] + \
+ in1[i][2]*in2[2][j] + \
+ in1[i][3]*in2[3][j])
+
+ out[0][0] = MULT(0,0);
+ out[0][1] = MULT(0,1);
+ out[0][2] = MULT(0,2);
+ out[0][3] = MULT(0,3);
+ out[1][0] = MULT(1,0);
+ out[1][1] = MULT(1,1);
+ out[1][2] = MULT(1,2);
+ out[1][3] = MULT(1,3);
+ out[2][0] = MULT(2,0);
+ out[2][1] = MULT(2,1);
+ out[2][2] = MULT(2,2);
+ out[2][3] = MULT(2,3);
+ out[3][0] = MULT(3,0);
+ out[3][1] = MULT(3,1);
+ out[3][2] = MULT(3,2);
+ out[3][3] = MULT(3,3);
+
+#undef MULT
+
+}
+
+void M_AngleMatrix( RadianEuler const &angles, const Vector &position, M_matrix4x4_t& matrix )
+{
+ // Assert( s_bMathlibInitialized );
+ float sx, sy, sz, cx, cy, cz;
+
+
+ sx = sinf(angles[0]);
+ cx = cosf(angles[0]);
+ sy = sinf(angles[1]);
+ cy = cosf(angles[1]);
+ sz = sinf(angles[2]);
+ cz = cosf(angles[2]);
+
+ // SinCos( angles[0], &sx, &cx ); // 2
+ // SinCos( angles[1], &sy, &cy ); // 1
+ // SinCos( angles[2], &sz, &cz ); // 0
+
+ M_matrix4x4_t mx, my, mz, temp1;
+
+ // rotation about x
+ mx[1][1] = cx;
+ mx[1][2] = sx;
+ mx[2][1] = -sx;
+ mx[2][2] = cx;
+
+ // rotation about y
+ my[0][0] = cy;
+ my[0][2] = -sy;
+ my[2][0] = sy;
+ my[2][2] = cy;
+
+ // rotation about z
+ mz[0][0] = cz;
+ mz[0][1] = sz;
+ mz[1][0] = -sz;
+ mz[1][1] = cz;
+
+ // z * y * x
+ M_ConcatTransforms(mx, my, temp1);
+ M_ConcatTransforms(temp1, mz, matrix);
+
+ // put position in
+ matrix[3][0] = position.x;
+ matrix[3][1] = position.y;
+ matrix[3][2] = position.z;
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Motion mapper functions
+//-----------------------------------------------------------------------------
+#define BONEAXIS 0
+#define BONEDIR 0
+#define BONESIDE 1
+#define BONEUP 2
+#define WORLDUP 2
+#define PRINTMAT(m) \
+ printf("\n%f %f %f %f\n", m[0][0], m[0][1], m[0][2], m[0][3]); \
+ printf("%f %f %f %f\n", m[1][0], m[1][1], m[1][2], m[1][3]); \
+ printf("%f %f %f %f\n", m[2][0], m[2][1], m[2][2], m[2][3]); \
+ printf("%f %f %f %f\n", m[3][0], m[3][1], m[3][2], m[3][3]);
+
+struct s_planeConstraint_t
+{
+ char jointNameString[1024];
+ float floor;
+ int axis;
+
+};
+
+struct s_iksolve_t
+{
+ char jointNameString[1024];
+ int reverseSolve;
+ float extremityScale;
+ Vector limbRootOffsetScale;
+ int doRelativeLock;
+ char relativeLockNameString[1024];
+ float relativeLockScale;
+
+};
+
+struct s_jointScale_t
+{
+ char jointNameString[1024];
+ float scale;
+};
+
+struct s_template_t
+{
+ char rootScaleJoint[1024];
+ float rootScaleAmount;
+ int numIKSolves;
+ s_iksolve_t *ikSolves[128];
+ int numJointScales;
+ s_jointScale_t *jointScales[128];
+ int numPlaneConstraints;
+ s_planeConstraint_t *planeConstraints[128];
+ float toeFloorZ;
+ int doSkeletonScale;
+ float skeletonScale;
+
+};
+
+
+//-----------------------------------------------------------------------------
+// Load a template file into structure
+//-----------------------------------------------------------------------------
+s_template_t *New_Template()
+{
+ s_template_t *pTemplate = (s_template_t *)kalloc(1, sizeof(s_template_t));
+ pTemplate->rootScaleAmount = 1.0;
+ pTemplate->numIKSolves = 0;
+ pTemplate->numJointScales = 0;
+ pTemplate->toeFloorZ = 2.802277;
+ pTemplate->numPlaneConstraints = 0;
+ pTemplate->doSkeletonScale = 0;
+ pTemplate->skeletonScale = 1.0;
+ return pTemplate;
+}
+s_iksolve_t *New_IKSolve()
+{
+ s_iksolve_t *pIKSolve = (s_iksolve_t *)kalloc(1, sizeof(s_iksolve_t));
+ pIKSolve->reverseSolve = 0;
+ pIKSolve->extremityScale = 1.0;
+ pIKSolve->limbRootOffsetScale[0] = pIKSolve->limbRootOffsetScale[1] = pIKSolve->limbRootOffsetScale[2] = 0.0;
+ pIKSolve->doRelativeLock = 0;
+ pIKSolve->relativeLockScale = 1.0;
+ return pIKSolve;
+}
+
+s_planeConstraint_t *New_planeConstraint(float floor)
+{
+ s_planeConstraint_t *pConstraint = (s_planeConstraint_t *)kalloc(1, sizeof(s_planeConstraint_t));
+ pConstraint->floor = floor;
+ pConstraint->axis = 2;
+
+ return pConstraint;
+}
+
+void Set_DefaultTemplate(s_template_t *pTemplate)
+{
+ pTemplate->numJointScales = 0;
+
+ strcpy(pTemplate->rootScaleJoint, "ValveBiped.Bip01_L_Foot");
+ pTemplate->rootScaleAmount = 1.0;
+
+ pTemplate->numIKSolves = 4;
+ pTemplate->ikSolves[0] = New_IKSolve();
+ pTemplate->ikSolves[1] = New_IKSolve();
+ pTemplate->ikSolves[2] = New_IKSolve();
+ pTemplate->ikSolves[3] = New_IKSolve();
+
+
+ pTemplate->numPlaneConstraints = 2;
+ pTemplate->planeConstraints[0] = New_planeConstraint(pTemplate->toeFloorZ);
+ strcpy(pTemplate->planeConstraints[0]->jointNameString, "ValveBiped.Bip01_L_Toe0");
+ pTemplate->planeConstraints[1] = New_planeConstraint(pTemplate->toeFloorZ);
+ strcpy(pTemplate->planeConstraints[1]->jointNameString, "ValveBiped.Bip01_R_Toe0");
+
+ strcpy(pTemplate->ikSolves[0]->jointNameString, "ValveBiped.Bip01_L_Foot");
+ pTemplate->ikSolves[0]->reverseSolve = 0;
+ pTemplate->ikSolves[0]->extremityScale = 1.0;
+ pTemplate->ikSolves[0]->limbRootOffsetScale[0] = 1.0;
+ pTemplate->ikSolves[0]->limbRootOffsetScale[1] = 1.0;
+ pTemplate->ikSolves[0]->limbRootOffsetScale[2] = 0.0;
+
+ strcpy(pTemplate->ikSolves[1]->jointNameString, "ValveBiped.Bip01_R_Foot");
+ pTemplate->ikSolves[1]->reverseSolve = 0;
+ pTemplate->ikSolves[1]->extremityScale = 1.0;
+ pTemplate->ikSolves[1]->limbRootOffsetScale[0] = 1.0;
+ pTemplate->ikSolves[1]->limbRootOffsetScale[1] = 1.0;
+ pTemplate->ikSolves[1]->limbRootOffsetScale[2] = 0.0;
+
+ strcpy(pTemplate->ikSolves[2]->jointNameString, "ValveBiped.Bip01_R_Hand");
+ pTemplate->ikSolves[2]->reverseSolve = 1;
+ pTemplate->ikSolves[2]->extremityScale = 1.0;
+ pTemplate->ikSolves[2]->limbRootOffsetScale[0] = 0.0;
+ pTemplate->ikSolves[2]->limbRootOffsetScale[1] = 0.0;
+ pTemplate->ikSolves[2]->limbRootOffsetScale[2] = 1.0;
+
+ strcpy(pTemplate->ikSolves[3]->jointNameString, "ValveBiped.Bip01_L_Hand");
+ pTemplate->ikSolves[3]->reverseSolve = 1;
+ pTemplate->ikSolves[3]->extremityScale = 1.0;
+ pTemplate->ikSolves[3]->limbRootOffsetScale[0] = 0.0;
+ pTemplate->ikSolves[3]->limbRootOffsetScale[1] = 0.0;
+ pTemplate->ikSolves[3]->limbRootOffsetScale[2] = 1.0;
+ // pTemplate->ikSolves[3]->doRelativeLock = 1;
+ // strcpy(pTemplate->ikSolves[3]->relativeLockNameString, "ValveBiped.Bip01_R_Hand");
+ // pTemplate->ikSolves[3]->relativeLockScale = 1.0;
+
+}
+
+void split(char *str, char *sep, char **sp)
+{
+ char *r = strtok(str, sep);
+ while(r != NULL)
+ {
+ *sp = r;
+ sp++;
+ r = strtok(NULL, sep);
+ }
+ *sp = NULL;
+}
+
+
+int checkCommand(char *str, char *cmd, int numOptions, int numSplit)
+{
+ if(strcmp(str, cmd) == 0)
+ {
+ if(numOptions <= numSplit)
+ return 1;
+ else
+ {
+ printf("Error: Number or argument mismatch in template file cmd %s, requires %i, found %i\n", cmd, numOptions, numSplit);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+s_template_t *Load_Template(char *name )
+{
+
+ // Sanity check file and init
+ Assert(name);
+
+ s_template_t *pTemplate = New_Template();
+
+
+ // Open file
+ if (!OpenGlobalFile( name ))
+ return 0;
+
+
+ //March through lines
+ g_iLinecount = 0;
+ while(fgets( g_szLine, sizeof( g_szLine ), g_fpInput ) != NULL)
+ {
+ g_iLinecount++;
+ if(g_szLine[0] == '#')
+ continue;
+
+ char *endP = strrchr(g_szLine, '\n');
+ if(endP != NULL)
+ *endP = '\0';
+
+
+ char *sp[128];
+ char **spp = sp;
+
+ char sep[] = " ";
+ split(g_szLine, sep, sp);
+ int numSplit = 0;
+
+ while(*spp != NULL)
+ {
+ spp++;
+ numSplit++;
+
+ }
+ if(numSplit < 1 ||
+ *sp[0] == '\n')
+ continue;
+
+
+ // int numRead = sscanf( g_szLine, "%s %s %s", cmd, &option, &option2 );
+
+ // // Blank line
+ // if ((numRead == EOF) || (numRead == 0))
+ // continue;
+
+ // commands
+ char *cmd;
+ int numOptions = numSplit - 1;
+
+ cmd = sp[0];
+ if(checkCommand(cmd, "twoJointIKSolve", 1, numOptions))
+ {
+ printf("\nCreating two joint IK solve %s\n", sp[1]);
+ pTemplate->ikSolves[pTemplate->numIKSolves] = New_IKSolve();
+ strcpy(pTemplate->ikSolves[pTemplate->numIKSolves]->jointNameString, sp[1]);
+ pTemplate->numIKSolves++;
+
+ }
+ else if(checkCommand(cmd, "oneJointPlaneConstraint", 1, numOptions))
+ {
+ printf("\nCreating one joint plane constraint %s\n", sp[1]);
+ pTemplate->planeConstraints[pTemplate->numPlaneConstraints] = New_planeConstraint(pTemplate->toeFloorZ);
+ strcpy(pTemplate->planeConstraints[pTemplate->numPlaneConstraints]->jointNameString, sp[1]);
+ pTemplate->numPlaneConstraints++;
+
+ }
+ else if(checkCommand(cmd, "reverseSolve", 1, numOptions))
+ {
+ printf("reverseSolve: %s\n", sp[1]);
+ pTemplate->ikSolves[pTemplate->numIKSolves - 1]->reverseSolve = atoi(sp[1]);
+ }
+ else if(checkCommand(cmd, "extremityScale", 1, numOptions))
+ {
+ printf("extremityScale: %s\n", sp[1]);
+ pTemplate->ikSolves[pTemplate->numIKSolves - 1]->extremityScale = atof(sp[1]);
+ }
+ else if(checkCommand(cmd, "limbRootOffsetScale", 3, numOptions))
+ {
+ printf("limbRootOffsetScale: %s %s %s\n", sp[1], sp[2], sp[3]);
+ pTemplate->ikSolves[pTemplate->numIKSolves - 1]->limbRootOffsetScale[0] = atof(sp[1]);
+ pTemplate->ikSolves[pTemplate->numIKSolves - 1]->limbRootOffsetScale[1] = atof(sp[2]);
+ pTemplate->ikSolves[pTemplate->numIKSolves - 1]->limbRootOffsetScale[2] = atof(sp[3]);
+ }
+ else if(checkCommand(cmd, "toeFloorZ", 1, numOptions))
+ {
+ printf("toeFloorZ: %s\n", sp[1]);
+ pTemplate->toeFloorZ = atof(sp[1]);
+ }
+ else if(checkCommand(cmd, "relativeLock", 2, numOptions))
+ {
+ printf("relativeLock: %s\n", sp[1]);
+ pTemplate->ikSolves[pTemplate->numIKSolves - 1]->doRelativeLock = 1;
+ strcpy(pTemplate->ikSolves[pTemplate->numIKSolves - 1]->relativeLockNameString, sp[1]);
+ pTemplate->ikSolves[pTemplate->numIKSolves - 1]->relativeLockScale = atof(sp[2]);
+
+ }
+ else if(checkCommand(cmd, "rootScaleJoint", 1, numOptions))
+ {
+ printf("\nrootScaleJoint: %s\n", sp[1]);
+ strcpy(pTemplate->rootScaleJoint, sp[1]);
+ }
+ else if(checkCommand(cmd, "rootScaleAmount", 1, numOptions))
+ {
+ printf("rootScaleAmount: %s\n", sp[1]);
+ pTemplate->rootScaleAmount = atof(sp[1]);
+ }
+ else if(checkCommand(cmd, "jointScale", 2, numOptions))
+ {
+ printf("\nCreating joint scale %s of %s\n", sp[1], sp[2]);
+ pTemplate->jointScales[pTemplate->numJointScales] = (s_jointScale_t *)kalloc(1, sizeof(s_jointScale_t));
+ strcpy(pTemplate->jointScales[pTemplate->numJointScales]->jointNameString, sp[1]);
+ pTemplate->jointScales[pTemplate->numJointScales]->scale = atof(sp[2]);
+ pTemplate->numJointScales++;
+ }
+ else if(checkCommand(cmd, "skeletonScale", 2, numOptions))
+ {
+ printf("\nCreating skeleton scale of %s\n", sp[1]);
+ pTemplate->doSkeletonScale = 1;
+ pTemplate->skeletonScale = atof(sp[1]);
+ }
+ else
+ {
+ MdlWarning("unknown studio command\n" );
+ }
+ }
+ fclose( g_fpInput );
+ return pTemplate;
+}
+
+//-----------------------------------------------------------------------------
+// get node index from node string name
+//-----------------------------------------------------------------------------
+int GetNodeIndex(s_source_t *psource, char *nodeName)
+{
+ for(int i = 0; i < psource->numbones; i++)
+ {
+ if(strcmp(nodeName, psource->localBone[i].name) == 0)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// get node index from node string name
+//-----------------------------------------------------------------------------
+void GetNodePath(s_source_t *psource, int startIndex, int endIndex, int *path)
+{
+ *path = endIndex;
+
+ s_node_t *nodes;
+ nodes = psource->localBone;
+ while(*path != startIndex)
+ {
+ int parent = nodes[*path].parent;
+ path++;
+ *path = parent;
+ }
+ path++;
+ *path = -1;
+}
+
+void SumBonePathTranslations(int *indexPath, s_bone_t *boneArray, Vector &resultVector, int rootOffset = 0)
+{
+
+ // walk the path
+ int *pathPtr = indexPath;
+ // M_matrix4x4_t matrixCum;
+
+ // find length of path
+ int length = 0;
+ while(*pathPtr != -1)
+ {
+ length++;
+ pathPtr++;
+ }
+
+ int l = length - (1 + rootOffset);
+
+ resultVector[0] = 0.0;
+ resultVector[1] = 0.0;
+ resultVector[2] = 0.0;
+
+ for(int i = l; i > -1; i--)
+ {
+ s_bone_t *thisBone = boneArray + indexPath[i];
+ resultVector += thisBone->pos;
+ }
+}
+
+void CatBonePath(int *indexPath, s_bone_t *boneArray, M_matrix4x4_t &resultMatrix, int rootOffset = 0)
+{
+
+ // walk the path
+ int *pathPtr = indexPath;
+ // M_matrix4x4_t matrixCum;
+
+ // find length of path
+ int length = 0;
+ while(*pathPtr != -1)
+ {
+ length++;
+ pathPtr++;
+ }
+
+ int l = length - (1 + rootOffset);
+
+ for(int i = l; i > -1; i--)
+ {
+ s_bone_t *thisBone = boneArray + indexPath[i];
+ // printf("bone index: %i %i\n", i, indexPath[i]);
+ // printf("pos: %f %f %f, rot: %f %f %f\n", thisBone->pos.x, thisBone->pos.y, thisBone->pos.z, thisBone->rot.x, thisBone->rot.y, thisBone->rot.z);
+ M_matrix4x4_t thisMatrix;
+ M_AngleMatrix(thisBone->rot, thisBone->pos, thisMatrix);
+ // PRINTMAT(thisMatrix)
+ M_matrix4x4_t tempCum;
+ M_MatrixCopy(resultMatrix, tempCum);
+ M_ConcatTransforms(thisMatrix, tempCum, resultMatrix);
+ }
+ // PRINTMAT(matrixCum);
+ // M_MatrixAngles(matrixCum, resultBone.rot, resultBone.pos);
+
+ // printf("pos: %f %f %f, rot: %f %f %f\n", resultBone.pos.x,resultBone.pos.y, resultBone.pos.z, RAD2DEG(resultBone.rot.x),RAD2DEG(resultBone.rot.y),RAD2DEG(resultBone.rot.z));
+
+}
+// int ConformSources(s_source_t *pSource, s_source_t *pTarget)
+// {
+ // if(pSource->numbones != *pTarget->numbones)
+ // {
+ // printf("ERROR: The number of bones in the target file must match the source file.");
+ // return 1;
+ // }
+ // if(pSource->numframes != pTarget->numframes)
+ // {
+ // printf("Note: Source and target frame lengths do not match");
+ // for(int t = 0; t < pTarget->numframes; t++)
+ // {
+ // free(pTarget->rawanim[t]);
+ // }
+ // pTarget->numframes = pSource->numframes;
+ // int size = pTarget->numbones * sizeof( s_bone_t );
+ // for(t = 0; t < pTarget->numframes; t++)
+ // {
+ // pTarget->rawanim[t] = (s_bone_t *) kalloc(1, size);
+ // memcpy((void *) pSource->rawanim[t], (void *) pTarget->rawanim[t], size
+ // }
+ // }
+ // pTarget->startframe = pSource->startframe;
+ // pTarget->endframe = pSource->endframe;
+
+
+
+
+void ScaleJointsFrame(s_source_t *pSkeleton, s_jointScale_t *jointScale, int t)
+{
+ int numBones = pSkeleton->numbones;
+
+ for(int i = 0; i < numBones; i++)
+ {
+ s_node_t pNode = pSkeleton->localBone[i];
+ s_bone_t *pSkelBone = &pSkeleton->rawanim[t][i];
+ if(strcmp(jointScale->jointNameString, pNode.name) == 0)
+ {
+ // printf("Scaling joint %s\n", pNode.name);
+ pSkelBone->pos = pSkelBone->pos * jointScale->scale;
+ }
+
+ }
+}
+void ScaleJoints(s_source_t *pSkeleton, s_jointScale_t *jointScale)
+{
+ int numFrames = pSkeleton->numframes;
+ for(int t = 0; t < numFrames; t++)
+ {
+ ScaleJointsFrame(pSkeleton, jointScale, t);
+ }
+}
+
+void ScaleSkeletonFrame(s_source_t *pSkeleton, float scale, int t)
+{
+ int numBones = pSkeleton->numbones;
+
+ for(int i = 0; i < numBones; i++)
+ {
+ s_bone_t *pSkelBone = &pSkeleton->rawanim[t][i];
+ pSkelBone->pos = pSkelBone->pos * scale;
+
+ }
+}
+void ScaleSkeleton(s_source_t *pSkeleton, float scale)
+{
+ int numFrames = pSkeleton->numframes;
+ for(int t = 0; t < numFrames; t++)
+ {
+ ScaleSkeletonFrame(pSkeleton, scale, t);
+ }
+}
+
+void CombineSkeletonAnimationFrame(s_source_t *pSkeleton, s_source_t *pAnimation, s_bone_t **ppAnim, int t)
+{
+ int numBones = pAnimation->numbones;
+ int size = numBones * sizeof( s_bone_t );
+ ppAnim[t] = (s_bone_t *) kalloc(1, size);
+ for(int i = 0; i < numBones; i++)
+ {
+ s_node_t pNode = pAnimation->localBone[i];
+ s_bone_t pAnimBone = pAnimation->rawanim[t][i];
+
+ if(pNode.parent > -1)
+ {
+ if ( i < pSkeleton->numbones )
+ {
+ s_bone_t pSkelBone = pSkeleton->rawanim[0][i];
+ ppAnim[t][i].pos = pSkelBone.pos;
+ }
+ else
+ {
+ if ( !g_bGaveMissingBoneWarning )
+ {
+ g_bGaveMissingBoneWarning = true;
+ Warning( "Warning: Target skeleton has less bones than source animation. Reverting to source data for extra bones.\n" );
+ }
+
+ ppAnim[t][i].pos = pAnimBone.pos;
+ }
+ }
+ else
+ {
+ ppAnim[t][i].pos = pAnimBone.pos;
+ }
+
+ ppAnim[t][i].rot = pAnimBone.rot;
+ }
+}
+void CombineSkeletonAnimation(s_source_t *pSkeleton, s_source_t *pAnimation, s_bone_t **ppAnim)
+{
+ int numFrames = pAnimation->numframes;
+ for(int t = 0; t < numFrames; t++)
+ {
+ CombineSkeletonAnimationFrame(pSkeleton, pAnimation, ppAnim, t);
+ }
+}
+
+
+//--------------------------------------------------------------------
+// MotionMap
+//--------------------------------------------------------------------
+s_source_t *MotionMap( s_source_t *pSource, s_source_t *pTarget, s_template_t *pTemplate )
+{
+
+ // scale skeleton
+ if(pTemplate->doSkeletonScale)
+ {
+ ScaleSkeleton(pTarget, pTemplate->skeletonScale);
+ }
+
+ // scale joints
+ for(int j = 0; j < pTemplate->numJointScales; j++)
+ {
+ s_jointScale_t *pJointScale = pTemplate->jointScales[j];
+ ScaleJoints(pTarget, pJointScale);
+ }
+
+
+ // root stuff
+ char rootString[128] = "ValveBiped.Bip01";
+
+ // !!! PARAMETER
+ int rootIndex = GetNodeIndex(pSource, rootString);
+ int rootScaleIndex = GetNodeIndex(pSource, pTemplate->rootScaleJoint);
+ int rootScalePath[512];
+ if(rootScaleIndex > -1)
+ {
+ GetNodePath(pSource, rootIndex, rootScaleIndex, rootScalePath);
+ }
+ else
+ {
+ printf("Error: Can't find node\n");
+ exit(0);
+ }
+ float rootScaleLengthSrc = pSource->rawanim[0][rootScaleIndex].pos[BONEDIR];
+ float rootScaleParentLengthSrc = pSource->rawanim[0][rootScalePath[1]].pos[BONEDIR];
+ float rootScaleSrc = rootScaleLengthSrc + rootScaleParentLengthSrc;
+ float rootScaleLengthTgt = pTarget->rawanim[0][rootScaleIndex].pos[BONEDIR];
+ float rootScaleParentLengthTgt = pTarget->rawanim[0][rootScalePath[1]].pos[BONEDIR];
+ float rootScaleTgt = rootScaleLengthTgt + rootScaleParentLengthTgt;
+ float rootScaleFactor = rootScaleTgt / rootScaleSrc;
+
+ if(g_verbose)
+ printf("Root Scale Factor: %f\n", rootScaleFactor);
+
+
+ // root scale origin
+ float toeFloorZ = pTemplate->toeFloorZ;
+ Vector rootScaleOrigin = pSource->rawanim[0][rootIndex].pos;
+ rootScaleOrigin[2] = toeFloorZ;
+
+
+ // setup workspace
+ s_bone_t *combinedRefAnimation[MAXSTUDIOANIMFRAMES];
+ s_bone_t *combinedAnimation[MAXSTUDIOANIMFRAMES];
+ s_bone_t *sourceAnimation[MAXSTUDIOANIMFRAMES];
+ CombineSkeletonAnimation(pTarget, pSource, combinedAnimation);
+ CombineSkeletonAnimation(pTarget, pSource, combinedRefAnimation);
+
+
+ // do source and target sanity checking
+ int sourceNumFrames = pSource->numframes;
+
+
+ // iterate through limb solves
+ for(int t = 0; t < sourceNumFrames; t++)
+ {
+ // setup pTarget for skeleton comparison
+ pTarget->rawanim[t] = combinedRefAnimation[t];
+
+ printf("Note: Processing frame: %i\n", t);
+ for(int ii = 0; ii < pTemplate->numIKSolves; ii++)
+ {
+ s_iksolve_t *thisSolve = pTemplate->ikSolves[ii];
+
+ char *thisJointNameString = thisSolve->jointNameString;
+ int thisJointIndex = GetNodeIndex(pSource, thisJointNameString);
+
+ // init paths to feet
+ int thisJointPathInRoot[512];
+
+ // get paths to feet
+ if(thisJointIndex > -1)
+ {
+ GetNodePath(pSource, rootIndex, thisJointIndex, thisJointPathInRoot);
+ }
+ else
+ {
+ printf("Error: Can't find node: %s\n" , thisJointNameString);
+ exit(0);
+ }
+
+ // leg "root" or thigh pointers
+ //int gParentIndex = thisJointPathInRoot[2];
+ int *gParentPath = thisJointPathInRoot + 2;
+
+ //----------------------------------------------------------------
+ // get limb lengths
+ //----------------------------------------------------------------
+ float thisJointLengthSrc = pSource->rawanim[0][thisJointIndex].pos[BONEDIR];
+ float parentJointLengthSrc = pSource->rawanim[0][thisJointPathInRoot[1]].pos[BONEDIR];
+
+ float thisLimbLengthSrc = thisJointLengthSrc + parentJointLengthSrc;
+
+ float thisJointLengthTgt = pTarget->rawanim[0][thisJointIndex].pos[BONEDIR];
+ float parentJointLengthTgt = pTarget->rawanim[0][thisJointPathInRoot[1]].pos[BONEDIR];
+
+ float thisLimbLengthTgt = thisJointLengthTgt + parentJointLengthTgt;
+
+ // Factor leg length delta
+ float thisLimbLength = thisLimbLengthSrc - thisLimbLengthTgt;
+ float thisLimbLengthFactor = thisLimbLengthTgt / thisLimbLengthSrc;
+
+ if(g_verbose)
+ printf("limb length %s: %i: %f, factor %f\n", thisJointNameString, thisJointIndex, thisLimbLength, thisLimbLengthFactor);
+
+ // calculate joint grandparent offset
+ // Note: because there's no reference pose this doesn't take rotation into account.
+ // This only works because of the assumption that joint translations aren't animated.
+ M_matrix4x4_t gParentGlobalMatSrc, gParentGlobalMatTgt;
+ Vector gParentGlobalSrc, gParentGlobalTgt;
+
+ // SumBonePathTranslations(gParentPath, pSource->rawanim[t], gParentGlobalSrc, 1);
+ // SumBonePathTranslations(gParentPath, pTarget->rawanim[t], gParentGlobalTgt, 1);
+
+ // get root path to source parent
+ CatBonePath(gParentPath, pSource->rawanim[t], gParentGlobalMatSrc, 1);
+ // check against reference animation
+ CatBonePath(gParentPath, pTarget->rawanim[t], gParentGlobalMatTgt, 1);
+
+ gParentGlobalSrc[0] = gParentGlobalMatSrc[3][0];
+ gParentGlobalSrc[1] = gParentGlobalMatSrc[3][1];
+ gParentGlobalSrc[2] = gParentGlobalMatSrc[3][2];
+
+ gParentGlobalTgt[0] = gParentGlobalMatTgt[3][0];
+ gParentGlobalTgt[1] = gParentGlobalMatTgt[3][1];
+ gParentGlobalTgt[2] = gParentGlobalMatTgt[3][2];
+
+
+ Vector gParentDelta(gParentGlobalTgt - gParentGlobalSrc);
+
+ if(g_verbose)
+ printf("Grand parent delta: %f %f %f\n", gParentDelta[0], gParentDelta[1], gParentDelta[2]);
+
+ gParentDelta *= thisSolve->limbRootOffsetScale;
+
+
+ //----------------------------------------------------------------
+ // time takes effect here
+ // above waste is unavoidable?
+ //----------------------------------------------------------------
+ M_matrix4x4_t rootMat;
+ M_AngleMatrix(pSource->rawanim[t][rootIndex].rot, pSource->rawanim[t][rootIndex].pos, rootMat);
+
+
+ // OK, time to get it together
+ // 1) scale foot by legLengthFactor in the non-translated thigh space
+ // 2) translate foot by legRootDelta in the space of the root
+ // do we leave everything in the space of the root then? PROBABLY!!
+
+ M_matrix4x4_t thisJointMat, parentJointMat, thisJointInGParentMat;
+ M_AngleMatrix(pSource->rawanim[t][thisJointPathInRoot[0]].rot, pSource->rawanim[t][thisJointPathInRoot[0]].pos, thisJointMat);
+ M_AngleMatrix(pSource->rawanim[t][thisJointPathInRoot[1]].rot, pSource->rawanim[t][thisJointPathInRoot[1]].pos, parentJointMat);
+ M_ConcatTransforms(thisJointMat, parentJointMat, thisJointInGParentMat);
+
+ if(!thisSolve->doRelativeLock)
+ {
+ // scale around grand parent
+ float effectiveScaleFactor = ((thisLimbLengthFactor - 1.0) * thisSolve->extremityScale ) + 1.0;
+ thisJointInGParentMat[3][0] *= effectiveScaleFactor;
+ thisJointInGParentMat[3][1] *= effectiveScaleFactor;
+ thisJointInGParentMat[3][2] *= effectiveScaleFactor;
+ }
+
+ // adjust into source root space
+ M_matrix4x4_t gParentInRootMat, thisJointInRootMat;
+ CatBonePath(gParentPath, pSource->rawanim[t], gParentInRootMat, 1);
+ M_ConcatTransforms(thisJointInGParentMat, gParentInRootMat, thisJointInRootMat);
+
+ if(!thisSolve->doRelativeLock)
+ {
+ // adjust by difference of local root
+ thisJointInRootMat[3][0] += gParentDelta[0];
+ thisJointInRootMat[3][1] += gParentDelta[1];
+ thisJointInRootMat[3][2] += gParentDelta[2];
+ }
+ else
+ {
+ char *relativeJointNameString = thisSolve->relativeLockNameString;
+ int relativeJointIndex = GetNodeIndex(pSource, relativeJointNameString);
+
+ // init paths to feet
+ int relativeJointPathInRoot[512];
+
+ // get paths to feet
+ if(relativeJointIndex > -1)
+ {
+ GetNodePath(pSource, rootIndex, relativeJointIndex, relativeJointPathInRoot);
+ }
+ else
+ {
+ printf("Error: Can't find node: %s\n" , relativeJointNameString);
+ exit(0);
+ }
+ // get the source relative joint
+ M_matrix4x4_t relativeJointInRootMatSrc, relativeJointInRootMatSrcInverse, thisJointInRelativeSrcMat;
+ CatBonePath(relativeJointPathInRoot, pSource->rawanim[t], relativeJointInRootMatSrc, 1);
+ M_MatrixInvert(relativeJointInRootMatSrc, relativeJointInRootMatSrcInverse);
+ M_ConcatTransforms(thisJointInRootMat, relativeJointInRootMatSrcInverse, thisJointInRelativeSrcMat);
+ if(thisSolve->relativeLockScale != 1.0)
+ {
+ thisJointInRelativeSrcMat[3][0] *= thisSolve->relativeLockScale;
+ thisJointInRelativeSrcMat[3][1] *= thisSolve->relativeLockScale;
+ thisJointInRelativeSrcMat[3][2] *= thisSolve->relativeLockScale;
+ }
+
+ // swap momentarily to get new destination
+ // NOTE: the relative lock must have already been solved
+ sourceAnimation[t] = pSource->rawanim[t];
+ pSource->rawanim[t] = combinedAnimation[t];
+
+ // get new relative location
+ M_matrix4x4_t relativeJointInRootMatTgt;
+ CatBonePath(relativeJointPathInRoot, pSource->rawanim[t], relativeJointInRootMatTgt, 1);
+ M_ConcatTransforms(thisJointInRelativeSrcMat, relativeJointInRootMatTgt, thisJointInRootMat);
+
+ // swap back just for cleanliness
+ // a little overkill as it's just swapped
+ // just leaving it here for clarity
+ combinedAnimation[t] = pSource->rawanim[t];
+ pSource->rawanim[t] = sourceAnimation[t];
+
+ }
+
+ //----------------------------------------------------------------
+ // swap animation
+ //----------------------------------------------------------------
+ sourceAnimation[t] = pSource->rawanim[t];
+ pSource->rawanim[t] = combinedAnimation[t];
+
+ //----------------------------------------------------------------
+ // make thigh data global based on new skeleton
+ //----------------------------------------------------------------
+ // get thigh in global space
+ M_matrix4x4_t gParentInTgtRootMat, ggParentInTgtRootMat;
+ // int *gParentPath = thisJointPathInRoot + 2;
+ CatBonePath(gParentPath, pSource->rawanim[t], gParentInTgtRootMat, 1);
+ CatBonePath(gParentPath+1, pSource->rawanim[t], ggParentInTgtRootMat, 1);
+
+
+ //----------------------------------------------------------------
+ // Calculate IK for legs
+ //----------------------------------------------------------------
+ float parentJointLength = pSource->rawanim[t][*(thisJointPathInRoot + 1)].pos[BONEDIR];
+ float thisJointLength = pSource->rawanim[t][thisJointIndex].pos[BONEDIR];
+
+ Vector thisLimbHypot;
+ thisLimbHypot[0] = thisJointInRootMat[3][0] - gParentInTgtRootMat[3][0];
+ thisLimbHypot[1] = thisJointInRootMat[3][1] - gParentInTgtRootMat[3][1];
+ thisLimbHypot[2] = thisJointInRootMat[3][2] - gParentInTgtRootMat[3][2];
+
+ float thisLimbHypotLength = thisLimbHypot.Length();
+
+ // law of cosines!
+ float gParentCos = (thisLimbHypotLength*thisLimbHypotLength + parentJointLength*parentJointLength - thisJointLength*thisJointLength) / (2*parentJointLength*thisLimbHypotLength);
+ float parentCos = (parentJointLength*parentJointLength + thisJointLength*thisJointLength - thisLimbHypotLength*thisLimbHypotLength) / (2*parentJointLength*thisJointLength);
+
+ VectorNormalize(thisLimbHypot);
+
+ Vector thisLimbHypotUnit = thisLimbHypot;
+
+ M_matrix4x4_t gParentJointIKMat;
+ Vector gParentJointIKRot, gParentJointIKOrth;
+
+ gParentJointIKRot[0] = gParentInTgtRootMat[BONEUP][0];
+ gParentJointIKRot[1] = gParentInTgtRootMat[BONEUP][1];
+ gParentJointIKRot[2] = gParentInTgtRootMat[BONEUP][2];
+
+ VectorNormalize(gParentJointIKRot);
+ gParentJointIKOrth = gParentJointIKRot.Cross(thisLimbHypotUnit);
+ VectorNormalize(gParentJointIKOrth);
+ gParentJointIKRot = thisLimbHypotUnit.Cross(gParentJointIKOrth);
+ VectorNormalize(gParentJointIKRot);
+
+ M_MatrixCopy(gParentInTgtRootMat, gParentJointIKMat);
+
+ gParentJointIKMat[0][0] = thisLimbHypotUnit[0];
+ gParentJointIKMat[0][1] = thisLimbHypotUnit[1];
+ gParentJointIKMat[0][2] = thisLimbHypotUnit[2];
+
+ gParentJointIKMat[1][0] = gParentJointIKOrth[0];
+ gParentJointIKMat[1][1] = gParentJointIKOrth[1];
+ gParentJointIKMat[1][2] = gParentJointIKOrth[2];
+
+ gParentJointIKMat[2][0] = gParentJointIKRot[0];
+ gParentJointIKMat[2][1] = gParentJointIKRot[1];
+ gParentJointIKMat[2][2] = gParentJointIKRot[2];
+
+
+ M_matrix4x4_t gParentJointIKRotMat, gParentJointResultMat;
+ float gParentDeg;
+ if(thisSolve->reverseSolve)
+ {
+ gParentDeg = acos(gParentCos);
+ }
+ else
+ {
+ gParentDeg = -acos(gParentCos);
+ }
+
+ // sanity check limb length
+ if(thisLimbHypotLength < thisLimbLengthTgt)
+ {
+ M_RotateZMatrix(gParentDeg, gParentJointIKRotMat);
+ }
+
+ M_ConcatTransforms(gParentJointIKRotMat, gParentJointIKMat, gParentJointResultMat);
+
+ M_matrix4x4_t parentJointIKRotMat;
+ //!!! shouldn't need the 180 degree addition, something in the law of cosines!!!
+ float parentDeg;
+ if(thisSolve->reverseSolve)
+ {
+ parentDeg = acos(parentCos)+M_PI;
+ }
+ else
+ {
+ parentDeg = -acos(parentCos)+M_PI;
+ }
+
+ // sanity check limb length
+ if(thisLimbHypotLength < thisLimbLengthTgt)
+ {
+ M_RotateZMatrix(parentDeg, parentJointIKRotMat);
+ }
+
+
+ // Thighs
+ M_matrix4x4_t ggParentInTgtRootMatInverse, gParentJointLocalMat;
+ M_MatrixInvert(ggParentInTgtRootMat, ggParentInTgtRootMatInverse);
+ M_ConcatTransforms(gParentJointResultMat, ggParentInTgtRootMatInverse, gParentJointLocalMat);
+
+ s_bone_t resultBone;
+
+ // temp test stuff
+ // M_MatrixAngles(thisJointInRootMat, resultBone.rot, resultBone.pos);
+ // pSource->rawanim[t][thisJointIndex].rot = resultBone.rot;
+ // pSource->rawanim[t][thisJointIndex].pos = resultBone.pos;
+
+ // M_MatrixAngles(gParentInTgtRootMat, resultBone.rot, resultBone.pos);
+ // pSource->rawanim[t][gParentIndex].rot = resultBone.rot;
+ // pSource->rawanim[t][gParentIndex].pos = resultBone.pos;
+
+
+ M_MatrixAngles(gParentJointLocalMat, resultBone.rot, resultBone.pos);
+ pSource->rawanim[t][*gParentPath].pos = resultBone.pos;
+ pSource->rawanim[t][*gParentPath].rot = resultBone.rot;
+
+ M_MatrixAngles(parentJointIKRotMat, resultBone.rot, resultBone.pos);
+ pSource->rawanim[t][*(thisJointPathInRoot+1)].rot = resultBone.rot;
+
+ M_matrix4x4_t parentJointGlobalMat, parentJointGlobalMatInverse, thisJointLocalMat;
+ CatBonePath(thisJointPathInRoot+1, pSource->rawanim[t], parentJointGlobalMat, 1);
+
+
+ M_MatrixInvert(parentJointGlobalMat, parentJointGlobalMatInverse);
+ M_ConcatTransforms(thisJointInRootMat, parentJointGlobalMatInverse, thisJointLocalMat);
+
+ M_MatrixAngles(thisJointLocalMat, resultBone.rot, resultBone.pos);
+ pSource->rawanim[t][thisJointIndex].rot = resultBone.rot;
+
+
+ // swap animation back for next solve
+ combinedAnimation[t] = pSource->rawanim[t];
+ pSource->rawanim[t] = sourceAnimation[t];
+
+ }
+ // swap animation
+ sourceAnimation[t] = pSource->rawanim[t];
+ pSource->rawanim[t] = combinedAnimation[t];
+
+ //----------------------------------------------------------------
+ // adjust root
+ //----------------------------------------------------------------
+ Vector originBonePos = pSource->rawanim[t][rootIndex].pos;
+ Vector rootInScaleOrigin = originBonePos - rootScaleOrigin;
+ float effectiveRootScale = ((rootScaleFactor - 1.0) * pTemplate->rootScaleAmount) + 1.0;
+ Vector scaledRoot = rootInScaleOrigin * effectiveRootScale;
+ pSource->rawanim[t][rootIndex].pos = rootScaleOrigin + scaledRoot;
+
+ //------------------------------------------------------------
+ // plane constraints
+ //------------------------------------------------------------
+ for(int ii = 0; ii < pTemplate->numPlaneConstraints; ii++)
+ {
+ s_planeConstraint_t *thisSolve = pTemplate->planeConstraints[ii];
+
+ char *thisJointNameString = thisSolve->jointNameString;
+ if(g_verbose)
+ printf("Executing plane constraint: %s\n", thisJointNameString);
+
+ int thisJointIndex = GetNodeIndex(pSource, thisJointNameString);
+
+ // init paths to feet
+ int thisJointPath[512];
+
+ // get paths to feet
+ if(thisJointIndex > -1)
+ {
+ GetNodePath(pSource, -1, thisJointIndex, thisJointPath);
+ }
+ else
+ {
+ printf("Error: Can't find node: %s\n" , thisJointNameString);
+ exit(0);
+ }
+ int parentIndex = thisJointPath[1];
+ int *parentPath = thisJointPath + 1;
+
+ M_matrix4x4_t thisJointGlobalMat, parentJointGlobalMat, gParentJointGlobalMat, gParentJointGlobalMatInverse;
+ CatBonePath(thisJointPath, pSource->rawanim[t], thisJointGlobalMat, 0);
+ CatBonePath(parentPath, pSource->rawanim[t], parentJointGlobalMat, 0);
+ CatBonePath(parentPath+1, pSource->rawanim[t], gParentJointGlobalMat, 0);
+ M_MatrixInvert(gParentJointGlobalMat, gParentJointGlobalMatInverse);
+
+ if(thisJointGlobalMat[3][thisSolve->axis] < thisSolve->floor)
+ {
+ // printf("-- broken plane: %f\n", thisJointGlobalMat[3][thisSolve->axis]);
+ if(parentJointGlobalMat[3][thisSolve->axis] < thisSolve->floor)
+ {
+ printf("Error: Constraint parent has broken the plane, this frame's plane constraint unsolvable!\n");
+ }
+ else
+ {
+ Vector parentJointAtPlane(parentJointGlobalMat[3][0], parentJointGlobalMat[3][1], parentJointGlobalMat[3][2]);
+ Vector parentPos(parentJointGlobalMat[3][0], parentJointGlobalMat[3][1], parentJointGlobalMat[3][2]);
+ Vector thisJointAtPlane(thisJointGlobalMat[3][0], thisJointGlobalMat[3][1], thisJointGlobalMat[3][2]);
+ Vector thisJointPos(thisJointGlobalMat[3][0], thisJointGlobalMat[3][1], thisJointGlobalMat[3][2]);
+
+ thisJointAtPlane[thisSolve->axis] = thisSolve->floor;
+ parentJointAtPlane[thisSolve->axis] = thisSolve->floor;
+
+ float thisJointLength = pSource->rawanim[t][thisJointIndex].pos[BONEAXIS];
+ float parentLengthToPlane = parentPos[thisSolve->axis] - thisSolve->floor;
+ float adjacent = sqrtf((thisJointLength * thisJointLength) - (parentLengthToPlane * parentLengthToPlane));
+ Vector parentDirection = thisJointAtPlane - parentJointAtPlane;
+ VectorNormalize(parentDirection);
+
+ Vector newJointPos = parentJointAtPlane + (parentDirection * adjacent);
+
+ Vector newParentDir = newJointPos - parentPos;
+ Vector parentUp(parentJointGlobalMat[BONEUP][0], parentJointGlobalMat[BONEUP][1], parentJointGlobalMat[BONEUP][2]);
+
+ VectorNormalize(newParentDir);
+ VectorNormalize(parentUp);
+ // Vector parentSide = newParentDir.Cross(parentUp);
+ Vector parentSide = parentUp.Cross(newParentDir);
+ VectorNormalize(parentSide);
+ parentUp = newParentDir.Cross(parentSide);
+ // parentUp = parentSide.Cross(newParentDir);
+ VectorNormalize(parentUp);
+ parentJointGlobalMat[BONEDIR][0] = newParentDir[0];
+ parentJointGlobalMat[BONEDIR][1] = newParentDir[1];
+ parentJointGlobalMat[BONEDIR][2] = newParentDir[2];
+ parentJointGlobalMat[BONEUP][0] = parentUp[0];
+ parentJointGlobalMat[BONEUP][1] = parentUp[1];
+ parentJointGlobalMat[BONEUP][2] = parentUp[2];
+ parentJointGlobalMat[BONESIDE][0] = parentSide[0];
+ parentJointGlobalMat[BONESIDE][1] = parentSide[1];
+ parentJointGlobalMat[BONESIDE][2] = parentSide[2];
+
+
+ M_matrix4x4_t newParentJointMat;
+
+ M_ConcatTransforms(parentJointGlobalMat, gParentJointGlobalMatInverse, newParentJointMat);
+
+ s_bone_t resultBone;
+ M_MatrixAngles(newParentJointMat, resultBone.rot, resultBone.pos);
+ pSource->rawanim[t][parentIndex].rot = resultBone.rot;
+ }
+ }
+ }
+
+ // swap animation back for next solve
+ combinedAnimation[t] = pSource->rawanim[t];
+ pSource->rawanim[t] = sourceAnimation[t];
+ }
+ for(int t = 0; t < sourceNumFrames; t++)
+ {
+ pTarget->rawanim[t] = combinedAnimation[t];
+ }
+ pTarget->numframes = sourceNumFrames;
+
+
+
+
+
+#if 0
+ // Process motion mapping into out and return that
+ s_source_t *out = new s_source_t;
+
+ return out;
+#else
+ // Just returns the start animation, to test the Save_SMD API.
+ return pTarget;
+#endif
+}
+
+char templates[] =
+"\n\
+#\n\
+# default template file is analogus to not specifying a template file at all\n\
+#\n\
+\n\
+rootScaleJoint ValveBiped.Bip01_L_Foot\n\
+rootScaleAmount 1.0\n\
+toeFloorZ 2.7777\n\
+\n\
+twoJointIKSolve ValveBiped.Bip01_L_Foot\n\
+reverseSolve 0\n\
+extremityScale 1.0\n\
+limbRootOffsetScale 1.0 1.0 0.0\n\
+\n\
+twoJointIKSolve ValveBiped.Bip01_R_Foot\n\
+reverseSolve 0\n\
+extremityScale 1.0\n\
+limbRootOffsetScale 1.0 1.0 0.0\n\
+\n\
+oneJointPlaneConstraint ValveBiped.Bip01_L_Toe0\n\
+\n\
+oneJointPlaneConstraint ValveBiped.Bip01_R_Toe0\n\
+\n\
+twoJointIKSolve ValveBiped.Bip01_R_Hand\n\
+reverseSolve 1\n\
+extremityScale 1.0\n\
+limbRootOffsetScale 0.0 0.0 1.0\n\
+\n\
+twoJointIKSolve ValveBiped.Bip01_L_Hand\n\
+reverseSolve 1\n\
+extremityScale 1.0\n\
+limbRootOffsetScale 0.0 0.0 1.0\n\
+\n\
+";
+
+
+void UsageAndExit()
+{
+ MdlError( "usage: motionmapper [-quiet] [-verbose] [-templateFile filename] [-printTemplates] sourceanim.smd targetskeleton.smd output.smd\n\
+\tsourceanim: should contain ref pose and animation data\n\
+\ttargetsekeleton: should contain new ref pose, animation data ignored/can be absent\n\
+\toutput: animation from source mapped onto target skeleton (contains new ref pose)\n\
+\t-templateFile filename : specifies a template file for guiding the mapping of motion\n\
+\t-printTemplate: Causes motionmapper to output the contents of an example template file, which can be used in conjunction with the -templateFile argument to create various motion effects.\n\
+\n");
+}
+
+void PrintHeader()
+{
+ vprint( 0, "Valve Software - motionmapper.exe ((c) Valve Coroporation %s)\n", __DATE__ );
+ vprint( 0, "--- Maps motion from one animation/skeleton onto another skeleton ---\n" );
+}
+
+
+
+/*
+==============
+main
+==============
+*/
+int main (int argc, char **argv)
+{
+ int i;
+
+ int useTemplate = 0;
+ char templateFileName[1024];
+
+ // Header
+ PrintHeader();
+
+ // Init command line stuff
+ CommandLine()->CreateCmdLine( argc, argv );
+ InstallSpewFunction();
+
+ // init math stuff
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+ g_currentscale = g_defaultscale = 1.0;
+ g_defaultrotation = RadianEuler( 0, 0, M_PI / 2 );
+
+ // No args?
+ if (argc == 1)
+ {
+ UsageAndExit();
+ }
+
+ // Init variable
+ g_quiet = false;
+
+ // list template hooey
+ CUtlVector< CUtlSymbol > filenames;
+
+ // Get args
+ for (i = 1; i < argc; i++)
+ {
+ // Switches
+ if (argv[i][0] == '-')
+ {
+ if (!stricmp(argv[i], "-allowdebug"))
+ {
+ // Ignore, used by interface system to catch debug builds checked into release tree
+ continue;
+ }
+
+ if (!stricmp(argv[i], "-quiet"))
+ {
+ g_quiet = true;
+ g_verbose = false;
+ continue;
+ }
+
+ if (!stricmp(argv[i], "-verbose"))
+ {
+ g_quiet = false;
+ g_verbose = true;
+ continue;
+ }
+ if (!stricmp(argv[i], "-printTemplate"))
+ {
+ printf("%s\n", templates);
+ exit(0);
+
+ }
+ if (!stricmp(argv[i], "-templateFile"))
+ {
+ if(i + 1 < argc)
+ {
+ strcpy( templateFileName, argv[i+1]);
+ useTemplate = 1;
+ printf("Note: %s passed as template file", templateFileName);
+ }
+ else
+ {
+ printf("Error: -templateFile requires an argument, none found!");
+ UsageAndExit();
+
+ }
+ i++;
+ continue;
+ }
+ }
+ else
+ {
+ // more template stuff
+ CUtlSymbol sym = argv[ i ];
+ filenames.AddToTail( sym );
+ }
+ }
+
+ // Enough file args?
+ if ( filenames.Count() != 3 )
+ {
+ // misformed arguments
+ // otherwise generating unintended results
+ printf("Error: 3 file arguments required, %i found!", filenames.Count());
+ UsageAndExit();
+ }
+
+ // Filename arg indexes
+ int sourceanim = 0;
+ int targetskel = 1;
+ int outputanim = 2;
+
+ // Copy arg string to global variable
+ strcpy( g_outfile, filenames[ outputanim ].String() );
+
+ // Init filesystem hooey
+ CmdLib_InitFileSystem( g_outfile );
+ // ??
+ Q_FileBase( g_outfile, g_outfile, sizeof( g_outfile ) );
+
+ // Verbose stuff
+ if (!g_quiet)
+ {
+ vprint( 0, "%s, %s, %s, path %s\n", qdir, gamedir, g_outfile );
+ }
+ // ??
+ Q_DefaultExtension(g_outfile, ".smd", sizeof( g_outfile ) );
+
+ // Verbose stuff
+ if (!g_quiet)
+ {
+ vprint( 0, "Source animation: %s\n", filenames[ sourceanim ].String() );
+ vprint( 0, "Target skeleton: %s\n", filenames[ targetskel ].String() );
+
+ vprint( 0, "Creating on \"%s\"\n", g_outfile);
+ }
+ // fullpath = EXTERNAL GLOBAL!!!???
+ strcpy( fullpath, g_outfile );
+ strcpy( fullpath, ExpandPath( fullpath ) );
+ strcpy( fullpath, ExpandArg( fullpath ) );
+
+ // Load source and target data
+ s_source_t *pSource = Load_Source( filenames[sourceanim].String(), "smd", false, false );
+ s_source_t *pTarget = Load_Source( filenames[targetskel].String(), "smd", false, false );
+
+
+ //
+ s_template_t *pTemplate = NULL;
+ if(useTemplate)
+ {
+ pTemplate = Load_Template(templateFileName);
+ }
+ else
+ {
+ printf("Note: No template file specified, using defaults settings.\n");
+
+ pTemplate = New_Template();
+ Set_DefaultTemplate(pTemplate);
+ }
+
+
+ // Process skeleton
+ s_source_t *pMappedAnimation = MotionMap( pSource, pTarget, pTemplate );
+
+
+ // Save output (ref skeleton & animation data);
+ Save_SMD( fullpath, pMappedAnimation );
+
+ Q_StripExtension( filenames[outputanim].String(), outname, sizeof( outname ) );
+
+ // Verbose stuff
+ if (!g_quiet)
+ {
+ vprint( 0, "\nCompleted \"%s\"\n", g_outfile);
+ }
+
+ return 0;
+}
+
diff --git a/mp/src/utils/motionmapper/motionmapper.h b/mp/src/utils/motionmapper/motionmapper.h new file mode 100644 index 00000000..0df3f8ee --- /dev/null +++ b/mp/src/utils/motionmapper/motionmapper.h @@ -0,0 +1,274 @@ +/***
+*
+//========= Copyright Valve Corporation, All rights reserved. ============//
+*
+* This product contains software technology licensed from Id
+* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
+* All Rights Reserved.
+*
+****/
+
+#include <stdio.h>
+#include "basetypes.h"
+#include "utlvector.h"
+#include "utlsymbol.h"
+#include "mathlib/vector.h"
+#include "studio.h"
+
+struct LodScriptData_t;
+
+#define IDSTUDIOHEADER (('T'<<24)+('S'<<16)+('D'<<8)+'I')
+ // little-endian "IDST"
+#define IDSTUDIOANIMGROUPHEADER (('G'<<24)+('A'<<16)+('D'<<8)+'I')
+ // little-endian "IDAG"
+
+
+#define STUDIO_QUADRATIC_MOTION 0x00002000
+
+#define MAXSTUDIOANIMFRAMES 2000 // max frames per animation
+#define MAXSTUDIOSEQUENCES 1524 // total sequences
+#define MAXSTUDIOSRCBONES 512 // bones allowed at source movement
+#define MAXSTUDIOBONEWEIGHTS 3
+#define MAXSTUDIONAME 128
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+EXTERN char outname[1024];
+EXTERN int numdirs;
+EXTERN char cddir[32][MAX_PATH];
+EXTERN char fullpath[1024];
+
+EXTERN float g_defaultscale;
+EXTERN float g_currentscale;
+EXTERN RadianEuler g_defaultrotation;
+
+
+EXTERN char defaulttexture[16][MAX_PATH];
+EXTERN char sourcetexture[16][MAX_PATH];
+
+EXTERN int numrep;
+
+EXTERN int flip_triangles;
+EXTERN float normal_blend;
+
+
+void *kalloc( int num, int size );
+
+struct s_trianglevert_t
+{
+ int vertindex;
+ int normindex; // index into normal array
+ int s,t;
+ float u,v;
+};
+
+struct s_boneweight_t
+{
+ int numbones;
+
+ int bone[MAXSTUDIOBONEWEIGHTS];
+ float weight[MAXSTUDIOBONEWEIGHTS];
+};
+
+
+struct s_vertexinfo_t
+{
+ // wtf is this doing here?
+ int material;
+
+ int firstref;
+ int lastref;
+
+ int flexmask;
+ int numflex;
+ int flexoffset;
+};
+
+struct s_tmpface_t
+{
+ int material;
+ unsigned long a, b, c;
+ unsigned long ta, tb, tc;
+ unsigned long na, nb, nc;
+};
+
+struct s_face_t
+{
+ unsigned long a, b, c;
+};
+
+struct s_node_t
+{
+ char name[MAXSTUDIONAME];
+ int parent;
+};
+
+
+struct s_bone_t
+{
+ Vector pos;
+ RadianEuler rot;
+};
+
+struct s_texture_t
+{
+ char name[MAX_PATH];
+ int flags;
+ int parent;
+ int material;
+ float width;
+ float height;
+ float dPdu;
+ float dPdv;
+};
+EXTERN s_texture_t g_texture[MAXSTUDIOSKINS];
+EXTERN int g_numtextures;
+EXTERN int g_material[MAXSTUDIOSKINS]; // link into texture array
+EXTERN int g_nummaterials;
+
+EXTERN float g_gamma;
+EXTERN int g_numskinref;
+EXTERN int g_numskinfamilies;
+EXTERN int g_skinref[256][MAXSTUDIOSKINS]; // [skin][skinref], returns texture index
+EXTERN int g_numtexturegroups;
+EXTERN int g_numtexturelayers[32];
+EXTERN int g_numtexturereps[32];
+EXTERN int g_texturegroup[32][32][32];
+
+struct s_mesh_t
+{
+ int numvertices;
+ int vertexoffset;
+
+ int numfaces;
+ int faceoffset;
+};
+
+
+struct s_vertanim_t
+{
+ int vertex;
+ float speed;
+ float side;
+ Vector pos;
+ Vector normal;
+};
+
+// processed aggregate lod pools
+struct s_loddata_t
+{
+ int numvertices;
+ s_boneweight_t *globalBoneweight;
+ s_vertexinfo_t *vertexInfo;
+ Vector *vertex;
+ Vector *normal;
+ Vector4D *tangentS;
+ Vector2D *texcoord;
+
+ int numfaces;
+ s_face_t *face;
+
+ s_mesh_t mesh[MAXSTUDIOSKINS];
+
+ // remaps verts from an lod's source mesh to this all-lod processed aggregate pool
+ int *pMeshVertIndexMaps[MAX_NUM_LODS];
+};
+
+// raw off-disk source files. Raw data should be not processed.
+struct s_source_t
+{
+ char filename[MAX_PATH];
+ int time; // time stamp
+
+ bool isActiveModel;
+
+ // local skeleton hierarchy
+ int numbones;
+ s_node_t localBone[MAXSTUDIOSRCBONES];
+ matrix3x4_t boneToPose[MAXSTUDIOSRCBONES]; // converts bone local data into initial pose data
+
+ // bone remapping
+ int boneflags[MAXSTUDIOSRCBONES]; // attachment, vertex, etc flags for this bone
+ int boneref[MAXSTUDIOSRCBONES]; // flags for this and child bones
+ int boneLocalToGlobal[MAXSTUDIOSRCBONES]; // bonemap : local bone to world bone mapping
+ int boneGlobalToLocal[MAXSTUDIOSRCBONES]; // boneimap : world bone to local bone mapping
+
+ int texmap[MAXSTUDIOSKINS*4]; // map local MAX materials to unique textures
+
+ // per material mesh
+ int nummeshes;
+ int meshindex[MAXSTUDIOSKINS]; // mesh to skin index
+ s_mesh_t mesh[MAXSTUDIOSKINS];
+
+ // model global copy of vertices
+ int numvertices;
+ s_boneweight_t *localBoneweight; // vertex info about local bone weighting
+ s_boneweight_t *globalBoneweight; // vertex info about global bone weighting
+ s_vertexinfo_t *vertexInfo; // generic vertex info
+ Vector *vertex;
+ Vector *normal;
+ Vector4D *tangentS;
+ Vector2D *texcoord;
+
+ int numfaces;
+ s_face_t *face; // vertex indexs per face
+
+ // raw skeletal animation
+ int numframes;
+ int startframe;
+ int endframe;
+ s_bone_t *rawanim[MAXSTUDIOANIMFRAMES]; // [frame][bones];
+
+ // vertex animation
+ int *vanim_mapcount; // local verts map to N target verts
+ int **vanim_map; // local vertices to target vertices mapping list
+ int *vanim_flag; // local vert does animate
+
+ int numvanims[MAXSTUDIOANIMFRAMES];
+ s_vertanim_t *vanim[MAXSTUDIOANIMFRAMES]; // [frame][vertex]
+
+ // processed aggregate lod data
+ s_loddata_t *pLodData;
+};
+
+
+EXTERN int g_numsources;
+EXTERN s_source_t *g_source[MAXSTUDIOSEQUENCES];
+
+EXTERN int is_v1support;
+
+EXTERN int g_numverts;
+EXTERN Vector g_vertex[MAXSTUDIOVERTS];
+EXTERN s_boneweight_t g_bone[MAXSTUDIOVERTS];
+
+EXTERN int g_numnormals;
+EXTERN Vector g_normal[MAXSTUDIOVERTS];
+
+EXTERN int g_numtexcoords;
+EXTERN Vector2D g_texcoord[MAXSTUDIOVERTS];
+
+EXTERN int g_numfaces;
+EXTERN s_tmpface_t g_face[MAXSTUDIOTRIANGLES];
+EXTERN s_face_t g_src_uface[MAXSTUDIOTRIANGLES]; // max res unified faces
+
+struct v_unify_t
+{
+ int refcount;
+ int lastref;
+ int firstref;
+ int v;
+ int m;
+ int n;
+ int t;
+ v_unify_t *next;
+};
+
+EXTERN v_unify_t *v_list[MAXSTUDIOVERTS];
+EXTERN v_unify_t v_listdata[MAXSTUDIOVERTS];
+EXTERN int numvlist;
+
+int SortAndBalanceBones( int iCount, int iMaxCount, int bones[], float weights[] );
+void Grab_Vertexanimation( s_source_t *psource );
+extern void BuildIndividualMeshes( s_source_t *psource );
diff --git a/mp/src/utils/nvtristriplib/nvtristrip.h b/mp/src/utils/nvtristriplib/nvtristrip.h new file mode 100644 index 00000000..65ad5ef8 --- /dev/null +++ b/mp/src/utils/nvtristriplib/nvtristrip.h @@ -0,0 +1,124 @@ +#ifndef NVTRISTRIP_H
+#define NVTRISTRIP_H
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#pragma comment(lib, "nvtristrip")
+
+////////////////////////////////////////////////////////////////////////////////////////
+// Public interface for stripifier
+////////////////////////////////////////////////////////////////////////////////////////
+
+//GeForce1 and 2 cache size
+#define CACHESIZE_GEFORCE1_2 16
+
+//GeForce3 cache size
+#define CACHESIZE_GEFORCE3 24
+
+enum PrimType
+{
+ PT_LIST,
+ PT_STRIP,
+ PT_FAN
+};
+
+struct PrimitiveGroup
+{
+ PrimType type;
+ unsigned int numIndices;
+ unsigned short* indices;
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+ PrimitiveGroup() : type(PT_STRIP), numIndices(0), indices(NULL) {}
+ ~PrimitiveGroup()
+ {
+ if(indices)
+ delete[] indices;
+ indices = NULL;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetCacheSize()
+//
+// Sets the cache size which the stripfier uses to optimize the data.
+// Controls the length of the generated individual strips.
+// This is the "actual" cache size, so 24 for GeForce3 and 16 for GeForce1/2
+// You may want to play around with this number to tweak performance.
+//
+// Default value: 16
+//
+void SetCacheSize(const unsigned int cacheSize);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetStitchStrips()
+//
+// bool to indicate whether to stitch together strips into one huge strip or not.
+// If set to true, you'll get back one huge strip stitched together using degenerate
+// triangles.
+// If set to false, you'll get back a large number of separate strips.
+//
+// Default value: true
+//
+void SetStitchStrips(const bool bStitchStrips);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetMinStripSize()
+//
+// Sets the minimum acceptable size for a strip, in triangles.
+// All strips generated which are shorter than this will be thrown into one big, separate list.
+//
+// Default value: 0
+//
+void SetMinStripSize(const unsigned int minSize);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// SetListsOnly()
+//
+// If set to true, will return an optimized list, with no strips at all.
+//
+// Default value: false
+//
+void SetListsOnly(const bool bListsOnly);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// GenerateStrips()
+//
+// in_indices: input index list, the indices you would use to render
+// in_numIndices: number of entries in in_indices
+// primGroups: array of optimized/stripified PrimitiveGroups
+// numGroups: number of groups returned
+//
+// Be sure to call delete[] on the returned primGroups to avoid leaking mem
+//
+void GenerateStrips(const unsigned short* in_indices, const unsigned int in_numIndices,
+ PrimitiveGroup** primGroups, unsigned short* numGroups);
+
+
+////////////////////////////////////////////////////////////////////////////////////////
+// RemapIndices()
+//
+// Function to remap your indices to improve spatial locality in your vertex buffer.
+//
+// in_primGroups: array of PrimitiveGroups you want remapped
+// numGroups: number of entries in in_primGroups
+// numVerts: number of vertices in your vertex buffer, also can be thought of as the range
+// of acceptable values for indices in your primitive groups.
+// remappedGroups: array of remapped PrimitiveGroups
+//
+// Note that, according to the remapping handed back to you, you must reorder your
+// vertex buffer.
+//
+// Credit goes to the MS Xbox crew for the idea for this interface.
+//
+void RemapIndices(const PrimitiveGroup* in_primGroups, const unsigned short numGroups,
+ const unsigned short numVerts, PrimitiveGroup** remappedGroups);
+
+#endif
\ No newline at end of file diff --git a/mp/src/utils/phonemeextractor/extractor_utils.cpp b/mp/src/utils/phonemeextractor/extractor_utils.cpp new file mode 100644 index 00000000..ba927f04 --- /dev/null +++ b/mp/src/utils/phonemeextractor/extractor_utils.cpp @@ -0,0 +1,28 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+#include <windows.h>
+#include <stdio.h>
+
+//-----------------------------------------------------------------------------
+// Purpose: converts an english string to unicode
+//-----------------------------------------------------------------------------
+int ConvertANSIToUnicode(const char *ansi, wchar_t *unicode, int unicodeBufferSize)
+{
+ return ::MultiByteToWideChar(CP_ACP, 0, ansi, -1, unicode, unicodeBufferSize);
+}
+
+char *va( const char *fmt, ... )
+{
+ va_list args;
+ static char output[4][1024];
+ static int outbuffer = 0;
+
+ outbuffer++;
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output[ outbuffer & 3 ], fmt, args );
+ return output[ outbuffer & 3 ];
+}
\ No newline at end of file diff --git a/mp/src/utils/phonemeextractor/phonemeextractor-2010.vcxproj b/mp/src/utils/phonemeextractor/phonemeextractor-2010.vcxproj new file mode 100644 index 00000000..4f444916 --- /dev/null +++ b/mp/src/utils/phonemeextractor/phonemeextractor-2010.vcxproj @@ -0,0 +1,268 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Phonemeextractor</ProjectName>
+ <ProjectGuid>{079933D6-F849-3176-49FC-D50E4B461AC4}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>phonemeextractor</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>phonemeextractor</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /wd4995</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;../common;../hlfaceposer;../sapi51/include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=phonemeextractor;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;PHONEMEEXTRACTOR_EXPORTS;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\phonemeextractor;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);odbc32.lib;odbccp32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\phonemeextractor.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/phonemeextractor.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin\phonemeextractors</Message>
+ <Command>if not exist "..\..\..\game\bin\phonemeextractors" mkdir "..\..\..\game\bin\phonemeextractors"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\phonemeextractors\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\phonemeextractors\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\phonemeextractors\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+ /wd4995</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;../common;../hlfaceposer;../sapi51/include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=phonemeextractor;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;PHONEMEEXTRACTOR_EXPORTS;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\phonemeextractor;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);odbc32.lib;odbccp32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\phonemeextractor.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/phonemeextractor.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin\phonemeextractors</Message>
+ <Command>if not exist "..\..\..\game\bin\phonemeextractors" mkdir "..\..\..\game\bin\phonemeextractors"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\phonemeextractors\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\phonemeextractors\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\phonemeextractors\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\sapi51\lib\i386\sapi.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="talkback.h" />
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h" />
+ <ClInclude Include="..\..\public\tier0\basetypes.h" />
+ <ClInclude Include="..\..\public\tier0\commonmacros.h" />
+ <ClInclude Include="..\..\public\tier0\dbg.h" />
+ <ClInclude Include="..\..\public\tier0\fasttimer.h" />
+ <ClInclude Include="..\..\public\appframework\IAppSystem.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ <ClInclude Include="..\..\public\phonemeconverter.h" />
+ <ClInclude Include="..\..\public\phonemeextractor\phonemeextractor.h" />
+ <ClInclude Include="..\..\public\tier0\platform.h" />
+ <ClInclude Include="..\..\public\tier0\protected_things.h" />
+ <ClInclude Include="..\..\public\sentence.h" />
+ <ClInclude Include="..\..\public\string_t.h" />
+ <ClInclude Include="..\..\public\tier1\strtools.h" />
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\tier1\utlvector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector2d.h" />
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h" />
+ <ClInclude Include="..\sapi51\Include\sapi.h" />
+ <ClInclude Include="..\sapi51\Include\sapiddk.h" />
+ <ClInclude Include="..\sapi51\Include\Spddkhlp.h" />
+ <ClInclude Include="..\sapi51\Include\spdebug.h" />
+ <ClInclude Include="..\sapi51\Include\sperror.h" />
+ <ClInclude Include="..\sapi51\Include\sphelper.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="extractor_utils.cpp" />
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\..\public\phonemeconverter.cpp" />
+ <ClCompile Include="phonemeextractor.cpp" />
+ <ClCompile Include="..\..\public\sentence.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/phonemeextractor/phonemeextractor-2010.vcxproj.filters b/mp/src/utils/phonemeextractor/phonemeextractor-2010.vcxproj.filters new file mode 100644 index 00000000..22dba2f7 --- /dev/null +++ b/mp/src/utils/phonemeextractor/phonemeextractor-2010.vcxproj.filters @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Public Header Files">
+ <UniqueIdentifier>{680EF60A-F852-B6F6-8E56-5693F8167FE5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="SAPI Header Files">
+ <UniqueIdentifier>{0339A88D-F26D-9E55-2AC4-F2CED0F7745A}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\sapi51\lib\i386\sapi.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="talkback.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\basetypes.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\commonmacros.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\dbg.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\fasttimer.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\appframework\IAppSystem.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\phonemeconverter.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\phonemeextractor\phonemeextractor.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\platform.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\protected_things.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\sentence.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\string_t.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\strtools.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlvector.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector2d.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h">
+ <Filter>Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\sapi51\Include\sapi.h">
+ <Filter>SAPI Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\sapi51\Include\sapiddk.h">
+ <Filter>SAPI Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\sapi51\Include\Spddkhlp.h">
+ <Filter>SAPI Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\sapi51\Include\spdebug.h">
+ <Filter>SAPI Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\sapi51\Include\sperror.h">
+ <Filter>SAPI Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\sapi51\Include\sphelper.h">
+ <Filter>SAPI Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="extractor_utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\phonemeconverter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="phonemeextractor.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\sentence.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/phonemeextractor/phonemeextractor.cpp b/mp/src/utils/phonemeextractor/phonemeextractor.cpp new file mode 100644 index 00000000..8dfc8439 --- /dev/null +++ b/mp/src/utils/phonemeextractor/phonemeextractor.cpp @@ -0,0 +1,1425 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// extracephonemes.cpp : Defines the entry point for the console application.
+//
+#define PROTECTED_THINGS_DISABLE
+
+#include "tier0/wchartypes.h"
+#include <stdio.h>
+#include <windows.h>
+#include <tchar.h>
+#include "sphelper.h"
+#include "spddkhlp.h"
+// ATL Header Files
+#include <atlbase.h>
+// Face poser and util includes
+#include "utlvector.h"
+#include "phonemeextractor/PhonemeExtractor.h"
+#include "PhonemeConverter.h"
+#include "sentence.h"
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "filesystem.h"
+
+// Extract phoneme grammar id
+#define EP_GRAM_ID 101
+// First rule of dynamic sentence rule set
+#define DYN_SENTENCERULE 102
+// # of milliseconds to allow for processing before timeout
+#define SR_WAVTIMEOUT 4000
+// Weight tag for rule to rule word/rule transitions
+#define CONFIDENCE_WEIGHT 0.0f
+
+//#define LOGGING 1
+#define LOGFILE "c:\\fp.log"
+
+void LogReset( void )
+{
+#if LOGGING
+ FILE *fp = fopen( LOGFILE, "w" );
+ if ( fp )
+ fclose( fp );
+#endif
+}
+
+char *va( const char *fmt, ... );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *words -
+//-----------------------------------------------------------------------------
+void LogWords( CSentence& sentence )
+{
+ Log( "Wordcount == %i\n", sentence.m_Words.Size() );
+
+ for ( int i = 0; i < sentence.m_Words.Size(); i++ )
+ {
+ const CWordTag *w = sentence.m_Words[ i ];
+ Log( "Word %s %u to %u\n", w->GetWord(), w->m_uiStartByte, w->m_uiEndByte );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *phonemes -
+//-----------------------------------------------------------------------------
+void LogPhonemes( CSentence& sentence )
+{
+ return;
+
+ Log( "Phonemecount == %i\n", sentence.CountPhonemes() );
+
+ for ( int i = 0; i < sentence.m_Words.Size(); i++ )
+ {
+ const CWordTag *w = sentence.m_Words[ i ];
+
+ for ( int j = 0; j < w->m_Phonemes.Size(); j++ )
+ {
+ const CPhonemeTag *p = w->m_Phonemes[ j ];
+ Log( "Phoneme %s %u to %u\n", p->GetTag(), p->m_uiStartByte, p->m_uiEndByte );
+ }
+ }
+}
+
+#define NANO_CONVERT 10000000.0f;
+
+//-----------------------------------------------------------------------------
+// Purpose: Walk list of words and phonemes and create phoneme tags in CSentence object
+// FIXME: Right now, phonemes are assumed to evenly space out across a word.
+// Input : *converter -
+// result -
+// sentence -
+//-----------------------------------------------------------------------------
+void EnumeratePhonemes( ISpPhoneConverter *converter, const ISpRecoResult* result, CSentence& sentence )
+{
+ USES_CONVERSION;
+
+ // Grab access to element container
+ ISpPhrase *phrase = ( ISpPhrase * )result;
+ if ( !phrase )
+ return;
+
+ SPPHRASE *pElements;
+ if ( !SUCCEEDED( phrase->GetPhrase( &pElements ) ) )
+ return;
+
+ // Only use it if it's better/same size as what we already had on-hand
+ if ( pElements->Rule.ulCountOfElements > 0 )
+ //(unsigned int)( sentence.m_Words.Size() - sentence.GetWordBase() ) )
+ {
+ sentence.ResetToBase();
+
+ // Walk list of words
+ for ( ULONG i = 0; i < pElements->Rule.ulCountOfElements; i++ )
+ {
+ unsigned int wordstart, wordend;
+
+ // Get start/end sample index
+ wordstart = pElements->pElements[i].ulAudioStreamOffset + (unsigned int)pElements->ullAudioStreamPosition;
+ wordend = wordstart + pElements->pElements[i].ulAudioSizeBytes;
+
+ // Create word tag
+ CWordTag *w = new CWordTag( W2T( pElements->pElements[i].pszDisplayText ) );
+ Assert( w );
+ w->m_uiStartByte = wordstart;
+ w->m_uiEndByte = wordend;
+
+ sentence.AddWordTag( w );
+
+ // Count # of phonemes in this word
+ SPPHONEID pstr[ 2 ];
+ pstr[ 1 ] = 0;
+ WCHAR wszPhoneme[ SP_MAX_PRON_LENGTH ];
+
+ const SPPHONEID *current;
+ SPPHONEID phoneme;
+ current = pElements->pElements[i].pszPronunciation;
+ float total_weight = 0.0f;
+ while ( 1 )
+ {
+ phoneme = *current++;
+ if ( !phoneme )
+ break;
+
+ pstr[ 0 ] = phoneme;
+ wszPhoneme[ 0 ] = L'\0';
+
+ converter->IdToPhone( pstr, wszPhoneme );
+
+ total_weight += WeightForPhoneme( W2A( wszPhoneme ) );
+ }
+
+ current = pElements->pElements[i].pszPronunciation;
+
+ // Decide # of bytes/phoneme weight
+ float psize = 0;
+ if ( total_weight )
+ {
+ psize = ( wordend - wordstart ) / total_weight;
+ }
+
+ int number = 0;
+
+ // Re-walk the phoneme list and create true phoneme tags
+ float startWeight = 0.0f;
+ while ( 1 )
+ {
+ phoneme = *current++;
+ if ( !phoneme )
+ break;
+
+ pstr[ 0 ] = phoneme;
+ wszPhoneme[ 0 ] = L'\0';
+
+ converter->IdToPhone( pstr, wszPhoneme );
+
+ CPhonemeTag *p = new CPhonemeTag( W2A( wszPhoneme ) );
+ Assert( p );
+
+ float weight = WeightForPhoneme( W2A( wszPhoneme ) );
+
+ p->m_uiStartByte = wordstart + (int)( startWeight * psize );
+ p->m_uiEndByte = p->m_uiStartByte + (int)( psize * weight );
+
+ startWeight += weight;
+
+ // Convert to IPA phoneme code
+ p->SetPhonemeCode( TextToPhoneme( p->GetTag() ) );
+
+ sentence.AddPhonemeTag( w, p );
+
+ number++;
+ }
+ }
+ }
+
+ // Free memory
+ ::CoTaskMemFree(pElements);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create rules for each word in the reference sentence
+//-----------------------------------------------------------------------------
+typedef struct
+{
+ int ruleId;
+ SPSTATEHANDLE hRule;
+ CSpDynamicString word;
+ char plaintext[ 256 ];
+} WORDRULETYPE;
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates start for word of sentence
+// Input : cpRecoGrammar -
+// *root -
+// *rules -
+// word -
+//-----------------------------------------------------------------------------
+void AddWordRule( ISpRecoGrammar* cpRecoGrammar, SPSTATEHANDLE *root, CUtlVector< WORDRULETYPE > *rules, CSpDynamicString& word )
+{
+ USES_CONVERSION;
+ HRESULT hr;
+ WORDRULETYPE *newrule;
+
+ int idx = (*rules).AddToTail();
+
+ newrule = &(*rules)[ idx ];
+
+ newrule->ruleId = DYN_SENTENCERULE + idx + 1;
+ newrule->word = word;
+
+ strcpy( newrule->plaintext, W2T( word ) );
+
+ // Create empty rule
+ hr = cpRecoGrammar->CreateNewState( *root, &newrule->hRule );
+ Assert( !FAILED( hr ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : cpRecoGrammar -
+// *from -
+// *to -
+//-----------------------------------------------------------------------------
+void AddWordTransitionRule( ISpRecoGrammar* cpRecoGrammar, WORDRULETYPE *from, WORDRULETYPE *to )
+{
+ USES_CONVERSION;
+
+ HRESULT hr;
+ Assert( from );
+
+ if ( from && !to )
+ {
+ OutputDebugString( va( "Transition from %s to TERM\r\n", from->plaintext ) );
+ }
+ else
+ {
+ OutputDebugString( va( "Transition from %s to %s\r\n", from->plaintext, to->plaintext ) );
+ }
+
+ hr = cpRecoGrammar->AddWordTransition( from->hRule, to ? to->hRule : NULL, (WCHAR *)from->word, NULL, SPWT_LEXICAL, CONFIDENCE_WEIGHT, NULL );
+ Assert( !FAILED( hr ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : cpRecoGrammar -
+// *from -
+// *to -
+//-----------------------------------------------------------------------------
+void AddOptionalTransitionRule( ISpRecoGrammar* cpRecoGrammar, WORDRULETYPE *from, WORDRULETYPE *to )
+{
+ USES_CONVERSION;
+
+ HRESULT hr;
+ Assert( from );
+
+ if ( from && !to )
+ {
+ OutputDebugString( va( "Opt transition from %s to TERM\r\n", from->plaintext ) );
+ }
+ else
+ {
+ OutputDebugString( va( "Opt transition from %s to %s\r\n", from->plaintext, to->plaintext ) );
+ }
+
+ hr = cpRecoGrammar->AddWordTransition( from->hRule, to ? to->hRule : NULL, NULL, NULL, SPWT_LEXICAL, CONFIDENCE_WEIGHT, NULL );
+ Assert( !FAILED( hr ) );
+}
+
+#define MAX_WORD_SKIP 1
+//-----------------------------------------------------------------------------
+// Purpose: Links together all word rule states into a sentence rule CFG
+// Input : singleword -
+// cpRecoGrammar -
+// *root -
+// *rules -
+//-----------------------------------------------------------------------------
+bool BuildRules( ISpRecoGrammar* cpRecoGrammar, SPSTATEHANDLE *root, CUtlVector< WORDRULETYPE > *rules )
+{
+ HRESULT hr;
+ WORDRULETYPE *rule, *next;
+
+ int numrules = (*rules).Size();
+
+ rule = &(*rules)[ 0 ];
+
+ // Add transition
+ hr = cpRecoGrammar->AddWordTransition( *root, rule->hRule, NULL, NULL, SPWT_LEXICAL, CONFIDENCE_WEIGHT, NULL );
+ Assert( !FAILED( hr ) );
+
+ for ( int i = 0; i < numrules; i++ )
+ {
+ rule = &(*rules)[ i ];
+ if ( i < numrules - 1 )
+ {
+ next = &(*rules)[ i + 1 ];
+ }
+ else
+ {
+ next = NULL;
+ }
+
+ AddWordTransitionRule( cpRecoGrammar, rule, next );
+ }
+
+ if ( numrules > 1 )
+ {
+ for ( int skip = 1; skip <= min( MAX_WORD_SKIP, numrules ); skip++ )
+ {
+ OutputDebugString( va( "Opt transition from Root to %s\r\n", (*rules)[ 0 ].plaintext ) );
+
+ hr = cpRecoGrammar->AddWordTransition( *root, (*rules)[ 0 ].hRule, NULL, NULL, SPWT_LEXICAL, CONFIDENCE_WEIGHT, NULL );
+
+ // Now build rules where you can skip 1 to N intervening words
+ for ( int i = 1; i < numrules; i++ )
+ {
+ // Start at the beginning?
+ rule = &(*rules)[ i ];
+ if ( i < numrules - skip )
+ {
+ next = &(*rules)[ i + skip ];
+ }
+ else
+ {
+ continue;
+ }
+
+ // Add transition
+ AddOptionalTransitionRule( cpRecoGrammar, rule, next );
+ }
+
+ // Go from final rule to end point
+ AddOptionalTransitionRule( cpRecoGrammar, rule, NULL );
+ }
+ }
+
+ // Store it
+ hr = cpRecoGrammar->Commit(NULL);
+ if ( FAILED( hr ) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Debugging, prints alternate list if one is created
+// Input : cpResult -
+// (*pfnPrint -
+//-----------------------------------------------------------------------------
+void PrintAlternates( ISpRecoResult* cpResult, void (*pfnPrint)( const char *fmt, ... ) )
+{
+ ISpPhraseAlt *rgPhraseAlt[ 32 ];
+ memset( rgPhraseAlt, 0, sizeof( rgPhraseAlt ) );
+
+ ULONG ulCount;
+
+ ISpPhrase *phrase = ( ISpPhrase * )cpResult;
+ if ( phrase )
+ {
+ SPPHRASE *pElements;
+ if ( SUCCEEDED( phrase->GetPhrase( &pElements ) ) )
+ {
+ if ( pElements->Rule.ulCountOfElements > 0 )
+ {
+ HRESULT hr = cpResult->GetAlternates(
+ pElements->Rule.ulFirstElement,
+ pElements->Rule.ulCountOfElements,
+ 32,
+ rgPhraseAlt,
+ &ulCount);
+
+ Assert( !FAILED( hr ) );
+
+ for ( ULONG r = 0 ; r < ulCount; r++ )
+ {
+ CSpDynamicString dstrText;
+ hr = rgPhraseAlt[ r ]->GetText( (ULONG)SP_GETWHOLEPHRASE, (ULONG)SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
+ Assert( !FAILED( hr ) );
+
+ pfnPrint( "[ ALT ]" );
+ pfnPrint( dstrText.CopyToChar() );
+ pfnPrint( "\r\n" );
+ }
+ }
+ }
+
+ }
+
+ for ( int i = 0; i < 32; i++ )
+ {
+ if ( rgPhraseAlt[ i ] )
+ {
+ rgPhraseAlt[ i ]->Release();
+ rgPhraseAlt[ i ] = NULL;
+ }
+ }
+}
+
+void PrintWordsAndPhonemes( CSentence& sentence, void (*pfnPrint)( const char *fmt, ... ) )
+{
+ char sz[ 256 ];
+ int i;
+
+ pfnPrint( "WORDS\r\n\r\n" );
+
+ for ( i = 0 ; i < sentence.m_Words.Size(); i++ )
+ {
+ CWordTag *word = sentence.m_Words[ i ];
+ if ( !word )
+ continue;
+
+ sprintf( sz, "<%u - %u> %s\r\n",
+ word->m_uiStartByte, word->m_uiEndByte, word->GetWord() );
+
+ pfnPrint( sz );
+
+ for ( int j = 0 ; j < word->m_Phonemes.Size(); j++ )
+ {
+ CPhonemeTag *phoneme = word->m_Phonemes[ j ];
+ if ( !phoneme )
+ continue;
+
+ sprintf( sz, " <%u - %u> %s\r\n",
+ phoneme->m_uiStartByte, phoneme->m_uiEndByte, phoneme->GetTag() );
+
+ pfnPrint( sz );
+ }
+ }
+
+ pfnPrint( "\r\n" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a wave file and a string of words "text", creates a CFG from the
+// sentence and stores the resulting words/phonemes in CSentence
+// Input : *wavname -
+// text -
+// sentence -
+// (*pfnPrint -
+// Output : SR_RESULT
+//-----------------------------------------------------------------------------
+SR_RESULT ExtractPhonemes( const char *wavname, CSpDynamicString& text, CSentence& sentence, void (*pfnPrint)( const char *fmt, ...) )
+{
+ // Assume failure
+ SR_RESULT result = SR_RESULT_ERROR;
+
+ if ( text.Length() <= 0 )
+ {
+ pfnPrint( "Error: no rule / text specified\n" );
+ return result;
+ }
+
+ USES_CONVERSION;
+ HRESULT hr;
+
+ CUtlVector < WORDRULETYPE > wordRules;
+
+ CComPtr<ISpStream> cpInputStream;
+ CComPtr<ISpRecognizer> cpRecognizer;
+ CComPtr<ISpRecoContext> cpRecoContext;
+ CComPtr<ISpRecoGrammar> cpRecoGrammar;
+ CComPtr<ISpPhoneConverter> cpPhoneConv;
+
+ // Create basic SAPI stream object
+ // NOTE: The helper SpBindToFile can be used to perform the following operations
+ hr = cpInputStream.CoCreateInstance(CLSID_SpStream);
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Stream object not installed?\n" );
+ return result;
+ }
+
+ CSpStreamFormat sInputFormat;
+
+ // setup stream object with wav file MY_WAVE_AUDIO_FILENAME
+ // for read-only access, since it will only be access by the SR engine
+ hr = cpInputStream->BindToFile(
+ T2W(wavname),
+ SPFM_OPEN_READONLY,
+ NULL,
+ sInputFormat.WaveFormatExPtr(),
+ SPFEI_ALL_EVENTS );
+
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: couldn't open wav file %s\n", wavname );
+ return result;
+ }
+
+ // Create in-process speech recognition engine
+ hr = cpRecognizer.CoCreateInstance(CLSID_SpInprocRecognizer);
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 In process recognizer object not installed?\n" );
+ return result;
+ }
+
+ // Create recognition context to receive events
+ hr = cpRecognizer->CreateRecoContext(&cpRecoContext);
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to create recognizer context\n" );
+ return result;
+ }
+
+ // Create a grammar
+ hr = cpRecoContext->CreateGrammar( EP_GRAM_ID, &cpRecoGrammar );
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to create recognizer grammar\n" );
+ return result;
+ }
+
+ LANGID englishID = 0x409; // 1033 decimal
+
+ bool userSpecified = false;
+ LANGID langID = SpGetUserDefaultUILanguage();
+
+ // Allow commandline override
+ if ( CommandLine()->FindParm( "-languageid" ) != 0 )
+ {
+ userSpecified = true;
+ langID = CommandLine()->ParmValue( "-languageid", langID );
+ }
+
+ // Create a phoneme converter ( so we can convert to IPA codes )
+ hr = SpCreatePhoneConverter( langID, NULL, NULL, &cpPhoneConv );
+ if ( FAILED( hr ) )
+ {
+ if ( langID != englishID )
+ {
+ if ( userSpecified )
+ {
+ pfnPrint( "Warning: SAPI 5.1 Unable to create phoneme converter for command line override -languageid %i\n", langID );
+ }
+ else
+ {
+ pfnPrint( "Warning: SAPI 5.1 Unable to create phoneme converter for default UI language %i\n",langID );
+ }
+
+ // Try english!!!
+ langID = englishID;
+ hr = SpCreatePhoneConverter( langID, NULL, NULL, &cpPhoneConv );
+ }
+
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to create phoneme converter for English language id %i\n", langID );
+ return result;
+ }
+ else
+ {
+ pfnPrint( "Note: SAPI 5.1 Falling back to use english -languageid %i\n", langID );
+ }
+ }
+ else if ( userSpecified )
+ {
+ pfnPrint( "Note: SAPI 5.1 Using user specified -languageid %i\n",langID );
+ }
+
+ SPSTATEHANDLE hStateRoot;
+ // create/re-create Root level rule of grammar
+ hr = cpRecoGrammar->GetRule(L"Root", 0, SPRAF_TopLevel | SPRAF_Active, TRUE, &hStateRoot);
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to create root rule\n" );
+ return result;
+ }
+
+ // Inactivate it so we can alter it
+ hr = cpRecoGrammar->SetRuleState( NULL, NULL, SPRS_INACTIVE );
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to deactivate grammar rules\n" );
+ return result;
+ }
+
+ // Create the rule set from the words in text
+ {
+ CSpDynamicString currentWord;
+ WCHAR *pos = ( WCHAR * )text;
+ WCHAR str[ 2 ];
+ str[1]= 0;
+
+ while ( *pos )
+ {
+ if ( *pos == L' ' /*|| *pos == L'.' || *pos == L'-'*/ )
+ {
+ // Add word to rule set
+ if ( currentWord.Length() > 0 )
+ {
+ AddWordRule( cpRecoGrammar, &hStateRoot, &wordRules, currentWord );
+ currentWord.Clear();
+ }
+ pos++;
+ continue;
+ }
+
+ // Skip anything that's inside a [ xxx ] pair.
+ if ( *pos == L'[' )
+ {
+ while ( *pos && *pos != L']' )
+ {
+ pos++;
+ }
+
+ if ( *pos )
+ {
+ pos++;
+ }
+ continue;
+ }
+
+ str[ 0 ] = *pos;
+
+ currentWord.Append( str );
+ pos++;
+ }
+
+ if ( currentWord.Length() > 0 )
+ {
+ AddWordRule( cpRecoGrammar, &hStateRoot, &wordRules, currentWord );
+ }
+
+ if ( wordRules.Size() <= 0 )
+ {
+ pfnPrint( "Error: Text %s contained no usable words\n", text );
+ return result;
+ }
+
+ // Build all word to word transitions in the grammar
+ if ( !BuildRules( cpRecoGrammar, &hStateRoot, &wordRules ) )
+ {
+ pfnPrint( "Error: Rule set for %s could not be generated\n", text );
+ return result;
+ }
+ }
+
+ // check for recognitions and end of stream event
+ const ULONGLONG ullInterest =
+ SPFEI(SPEI_RECOGNITION) | SPFEI(SPEI_END_SR_STREAM) | SPFEI(SPEI_FALSE_RECOGNITION) |
+ SPFEI(SPEI_PHRASE_START ) | SPFEI(SPEI_HYPOTHESIS ) | SPFEI(SPEI_INTERFERENCE) ;
+ hr = cpRecoContext->SetInterest( ullInterest, ullInterest );
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to set interest level\n" );
+ return result;
+ }
+ // use Win32 events for command-line style application
+ hr = cpRecoContext->SetNotifyWin32Event();
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to set win32 notify event\n" );
+ return result;
+ }
+ // connect wav input to recognizer
+ // SAPI will negotiate mismatched engine/input audio formats using system audio codecs, so second parameter is not important - use default of TRUE
+ hr = cpRecognizer->SetInput(cpInputStream, TRUE);
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to associate input stream\n" );
+ return result;
+ }
+
+ // Activate the CFG ( rather than using dictation )
+ hr = cpRecoGrammar->SetRuleState( NULL, NULL, SPRS_ACTIVE );
+ if ( FAILED( hr ) )
+ {
+ switch ( hr )
+ {
+ case E_INVALIDARG:
+ pfnPrint( "pszName is invalid or bad. Alternatively, pReserved is non-NULL\n" );
+ break;
+ case SP_STREAM_UNINITIALIZED:
+ pfnPrint( "ISpRecognizer::SetInput has not been called with the InProc recognizer\n" );
+ break;
+ case SPERR_UNINITIALIZED:
+ pfnPrint( "The object has not been properly initialized.\n");
+ break;
+ case SPERR_UNSUPPORTED_FORMAT:
+ pfnPrint( "Audio format is bad or is not recognized. Alternatively, the device driver may be busy by another application and cannot be accessed.\n" );
+ break;
+ case SPERR_NOT_TOPLEVEL_RULE:
+ pfnPrint( "The rule pszName exists, but is not a top-level rule.\n" );
+ break;
+ default:
+ pfnPrint( "Unknown error\n" );
+ break;
+ }
+ pfnPrint( "Error: SAPI 5.1 Unable to activate rule set\n" );
+ return result;
+ }
+
+ // while events occur, continue processing
+ // timeout should be greater than the audio stream length, or a reasonable amount of time expected to pass before no more recognitions are expected in an audio stream
+ BOOL fEndStreamReached = FALSE;
+ while (!fEndStreamReached && S_OK == cpRecoContext->WaitForNotifyEvent( SR_WAVTIMEOUT ))
+ {
+ CSpEvent spEvent;
+ // pull all queued events from the reco context's event queue
+
+ while (!fEndStreamReached && S_OK == spEvent.GetFrom(cpRecoContext))
+ {
+ // Check event type
+ switch (spEvent.eEventId)
+ {
+ case SPEI_INTERFERENCE:
+ {
+ SPINTERFERENCE interference = spEvent.Interference();
+
+ switch ( interference )
+ {
+ case SPINTERFERENCE_NONE:
+ pfnPrint( "[ I None ]\r\n" );
+ break;
+ case SPINTERFERENCE_NOISE:
+ pfnPrint( "[ I Noise ]\r\n" );
+ break;
+ case SPINTERFERENCE_NOSIGNAL:
+ pfnPrint( "[ I No Signal ]\r\n" );
+ break;
+ case SPINTERFERENCE_TOOLOUD:
+ pfnPrint( "[ I Too Loud ]\r\n" );
+ break;
+ case SPINTERFERENCE_TOOQUIET:
+ pfnPrint( "[ I Too Quiet ]\r\n" );
+ break;
+ case SPINTERFERENCE_TOOFAST:
+ pfnPrint( "[ I Too Fast ]\r\n" );
+ break;
+ case SPINTERFERENCE_TOOSLOW:
+ pfnPrint( "[ I Too Slow ]\r\n" );
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case SPEI_PHRASE_START:
+ pfnPrint( "Phrase Start\r\n" );
+ sentence.MarkNewPhraseBase();
+ break;
+
+ case SPEI_HYPOTHESIS:
+ case SPEI_RECOGNITION:
+ case SPEI_FALSE_RECOGNITION:
+ {
+ CComPtr<ISpRecoResult> cpResult;
+ cpResult = spEvent.RecoResult();
+
+ CSpDynamicString dstrText;
+ if (spEvent.eEventId == SPEI_FALSE_RECOGNITION)
+ {
+ dstrText = L"(Unrecognized)";
+
+ result = SR_RESULT_FAILED;
+
+ // It's possible that the failed recog might have more words, so see if that's the case
+ EnumeratePhonemes( cpPhoneConv, cpResult, sentence );
+ }
+ else
+ {
+ // Hypothesis or recognition success
+ cpResult->GetText( (ULONG)SP_GETWHOLEPHRASE, (ULONG)SP_GETWHOLEPHRASE, TRUE, &dstrText, NULL);
+
+ EnumeratePhonemes( cpPhoneConv, cpResult, sentence );
+
+ if ( spEvent.eEventId == SPEI_RECOGNITION )
+ {
+ result = SR_RESULT_SUCCESS;
+ }
+
+ pfnPrint( va( "%s%s\r\n", spEvent.eEventId == SPEI_HYPOTHESIS ? "[ Hypothesis ] " : "", dstrText.CopyToChar() ) );
+ }
+
+ cpResult.Release();
+ }
+ break;
+ // end of the wav file was reached by the speech recognition engine
+ case SPEI_END_SR_STREAM:
+ fEndStreamReached = TRUE;
+ break;
+ }
+
+ // clear any event data/object references
+ spEvent.Clear();
+ }// END event pulling loop - break on empty event queue OR end stream
+ }// END event polling loop - break on event timeout OR end stream
+
+ // Deactivate rule
+ hr = cpRecoGrammar->SetRuleState( NULL, NULL, SPRS_INACTIVE );
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to deactivate rule set\n" );
+ return result;
+ }
+
+ // close the input stream, since we're done with it
+ // NOTE: smart pointer will call SpStream's destructor, and consequently ::Close, but code may want to check for errors on ::Close operation
+ hr = cpInputStream->Close();
+ if ( FAILED( hr ) )
+ {
+ pfnPrint( "Error: SAPI 5.1 Unable to close input stream\n" );
+ return result;
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: HACK HACK: We have to delete the RecoContext key or sapi starts to train
+// itself on each iteration which was causing some problems.
+// Input : hKey -
+//-----------------------------------------------------------------------------
+void RecursiveRegDelKey(HKEY hKey)
+{
+ char keyname[256]={0};
+ DWORD namesize=256;
+
+ //base case: no subkeys when RegEnumKeyEx returns error on index 0
+ LONG lResult=RegEnumKeyEx(hKey,0,keyname,&namesize,NULL,NULL,NULL,NULL);
+ if (lResult!=ERROR_SUCCESS)
+ {
+ return;
+ }
+
+ do
+ {
+ HKEY subkey;
+ LONG lResult2;
+ LONG lDelResult;
+ lResult2=RegOpenKeyEx(hKey,keyname,0,KEY_ALL_ACCESS,&subkey);
+
+ if (lResult2==ERROR_SUCCESS)
+ {
+ RecursiveRegDelKey(subkey);
+
+ RegCloseKey(subkey);
+ lDelResult=RegDeleteKey(hKey,keyname);
+ namesize=256;
+ //use 0 in the next function call because when you delete one, the rest shift down!
+ lResult=RegEnumKeyEx(hKey,0,keyname,&namesize,NULL,NULL,NULL,NULL);
+ }
+
+ else
+ {
+ break;
+ }
+
+ } while (lResult!=ERROR_NO_MORE_ITEMS);
+}
+
+bool IsUseable( CWordTag *word )
+{
+ if ( word->m_uiStartByte || word->m_uiEndByte )
+ return true;
+
+ return false;
+}
+
+int FindLastUsableWord( CSentence& outwords )
+{
+ int numwords = outwords.m_Words.Size();
+ if ( numwords < 1 )
+ {
+ Assert( 0 );
+ return -1;
+ }
+
+ for ( int i = numwords-1; i >= 0; i-- )
+ {
+ CWordTag *check = outwords.m_Words[ i ];
+ if ( IsUseable( check ) )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+int FindFirstUsableWord( CSentence& outwords )
+{
+ int numwords = outwords.m_Words.Size();
+ if ( numwords < 1 )
+ {
+ Assert( 0 );
+ return -1;
+ }
+
+ for ( int i = 0; i < numwords; i++ )
+ {
+ CWordTag *check = outwords.m_Words[ i ];
+ if ( IsUseable( check ) )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Counts words which have either a valid start or end byte
+// Input : *outwords -
+// Output : int
+//-----------------------------------------------------------------------------
+int CountUsableWords( CSentence& outwords )
+{
+ int count = 0;
+ int numwords = outwords.m_Words.Size();
+ // Nothing to do
+ if ( numwords <= 0 )
+ return count;
+
+ for ( int i = 0; i < numwords; i++ )
+ {
+ CWordTag *word = outwords.m_Words[ i ];
+ if ( !IsUseable( word ) )
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Counts words which have either a valid start or end byte
+// Input : *outwords -
+// Output : int
+//-----------------------------------------------------------------------------
+int CountUnuseableWords( CSentence& outwords )
+{
+ int count = 0;
+ int numwords = outwords.m_Words.Size();
+ // Nothing to do
+ if ( numwords <= 0 )
+ return count;
+
+ for ( int i = 0; i < numwords; i++ )
+ {
+ CWordTag *word = outwords.m_Words[ i ];
+ if ( IsUseable( word ) )
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+// Keeps same relative spacing, but rebases list
+void RepartitionPhonemes( CWordTag *word, unsigned int oldStart, unsigned int oldEnd )
+{
+ // Repartition phonemes based on old range
+ float oldRange = ( float )( oldEnd - oldStart );
+ float newRange = ( float )( word->m_uiEndByte - word->m_uiStartByte );
+
+ for ( int i = 0; i < word->m_Phonemes.Size(); i++ )
+ {
+ CPhonemeTag *tag = word->m_Phonemes[ i ];
+ Assert( tag );
+
+ float frac1 = 0.0f, frac2 = 0.0f;
+ float delta1, delta2;
+
+ delta1 = ( float ) ( tag->m_uiStartByte - oldStart );
+ delta2 = ( float ) ( tag->m_uiEndByte - oldStart );
+ if ( oldRange > 0.0f )
+ {
+ frac1 = delta1 / oldRange;
+ frac2 = delta2 / oldRange;
+ }
+
+ tag->m_uiStartByte = word->m_uiStartByte + ( unsigned int ) ( frac1 * newRange );
+ tag->m_uiEndByte = word->m_uiStartByte + ( unsigned int ) ( frac2 * newRange );
+ }
+}
+
+void PartitionWords( CSentence& outwords, int start, int end, int sampleStart, int sampleEnd )
+{
+ int wordCount = end - start + 1;
+ Assert( wordCount >= 1 );
+ int stepSize = ( sampleEnd - sampleStart ) / wordCount;
+
+ int currentStart = sampleStart;
+
+ for ( int i = start; i <= end; i++ )
+ {
+ CWordTag *word = outwords.m_Words[ i ];
+ Assert( word );
+
+ unsigned int oldStart = word->m_uiStartByte;
+ unsigned int oldEnd = word->m_uiEndByte;
+
+ word->m_uiStartByte = currentStart;
+ word->m_uiEndByte = currentStart + stepSize;
+
+ RepartitionPhonemes( word, oldStart, oldEnd );
+
+ currentStart += stepSize;
+ }
+}
+
+void MergeWords( CWordTag *w1, CWordTag *w2 )
+{
+ unsigned int start, end;
+
+ start = min( w1->m_uiStartByte, w2->m_uiStartByte );
+ end = max( w1->m_uiEndByte, w2->m_uiEndByte );
+
+ unsigned int mid = ( start + end ) / 2;
+
+ unsigned int oldw1start, oldw2start, oldw1end, oldw2end;
+
+ oldw1start = w1->m_uiStartByte;
+ oldw2start = w2->m_uiStartByte;
+ oldw1end = w1->m_uiEndByte;
+ oldw2end = w2->m_uiEndByte;
+
+ w1->m_uiStartByte = start;
+ w1->m_uiEndByte = mid;
+ w2->m_uiStartByte = mid;
+ w2->m_uiEndByte = end;
+
+ RepartitionPhonemes( w1, oldw1start, oldw1end );
+ RepartitionPhonemes( w2, oldw2start, oldw2end );
+}
+
+void FixupZeroLengthWords( CSentence& outwords )
+{
+ while ( 1 )
+ {
+ int i;
+ for ( i = 0 ; i < outwords.m_Words.Size() - 1; i++ )
+ {
+ CWordTag *current, *next;
+
+ current = outwords.m_Words[ i ];
+ next = outwords.m_Words[ i + 1 ];
+
+ if ( current->m_uiEndByte - current->m_uiStartByte <= 0 )
+ {
+ MergeWords( current, next );
+ break;
+ }
+
+ if ( next->m_uiEndByte - next->m_uiStartByte <= 0 )
+ {
+ MergeWords( current, next );
+ break;
+ }
+ }
+
+ if ( i >= outwords.m_Words.Size() - 1 )
+ {
+ break;
+ }
+ }
+}
+
+void ComputeMissingByteSpans( int numsamples, CSentence& outwords )
+{
+ int numwords = outwords.m_Words.Size();
+ // Nothing to do
+ if ( numwords <= 0 )
+ return;
+
+ int interationcount = 1;
+
+ while( 1 )
+ {
+ Log( "\nCompute %i\n", interationcount++ );
+ LogWords( outwords );
+
+ int wordNumber;
+
+ // Done!
+ if ( !CountUnuseableWords( outwords ) )
+ {
+ FixupZeroLengthWords( outwords );
+ break;
+ }
+
+ if ( !CountUsableWords( outwords ) )
+ {
+ // Evenly space words across full sample time
+ PartitionWords( outwords, 0, numwords - 1, 0, numsamples );
+ break;
+ }
+
+ wordNumber = FindFirstUsableWord( outwords );
+ // Not the first word
+ if ( wordNumber > 0 )
+ {
+ // Repartition all of the unusables and the first one starting at zero over the range
+ CWordTag *firstUsable = outwords.m_Words[ wordNumber ];
+ Assert( firstUsable );
+
+ if ( firstUsable->m_uiStartByte != 0 )
+ {
+ PartitionWords( outwords, 0, wordNumber - 1, 0, firstUsable->m_uiStartByte );
+ }
+ else
+ {
+ PartitionWords( outwords, 0, wordNumber, 0, firstUsable->m_uiEndByte );
+ }
+
+ // Start over
+ continue;
+ }
+
+ wordNumber = FindLastUsableWord( outwords );
+ // Not the last word
+ if ( wordNumber >= 0 && wordNumber < numwords - 1 )
+ {
+ // Repartition all of the unusables and the first one starting at zero over the range
+ CWordTag *lastUsable = outwords.m_Words[ wordNumber ];
+ Assert( lastUsable );
+
+ if ( lastUsable->m_uiEndByte != (unsigned int)numsamples )
+ {
+ PartitionWords( outwords, wordNumber + 1, numwords-1, lastUsable->m_uiEndByte, numsamples );
+ }
+ else
+ {
+ PartitionWords( outwords, wordNumber, numwords-1, lastUsable->m_uiStartByte, numsamples );
+ }
+
+ // Start over
+ continue;
+ }
+
+ // If we get here it means that the start and end of the list are okay and we just have to
+ // iterate across the list and fix things in the middle
+ int startByte = 0;
+ int endByte = 0;
+ for ( int i = 0; i < numwords ; i++ )
+ {
+ CWordTag *word = outwords.m_Words[ i ];
+ if ( IsUseable( word ) )
+ {
+ startByte = word->m_uiEndByte;
+ continue;
+ }
+
+ // Found the start of a chain of 1 or more unusable words
+ // Find the startbyte of the next usable word and count how many words we check
+ int wordCount = 1;
+ for ( int j = i + 1; j < numwords; j++ )
+ {
+ CWordTag *next = outwords.m_Words[ j ];
+ if ( IsUseable( next ) )
+ {
+ endByte = next->m_uiStartByte;
+ break;
+ }
+
+ wordCount++;
+ }
+
+ // Now partition words across the gap and go to start again
+ PartitionWords( outwords, i, i + wordCount - 1, startByte, endByte );
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a wavfile and a list of inwords, determines the word/phonene
+// sample counts for the sentce
+// Input : *wavfile -
+// *inwords -
+// *outphonemes{ text.Clear( -
+// Output : SR_RESULT
+//-----------------------------------------------------------------------------
+static SR_RESULT SAPI_ExtractPhonemes(
+ const char *wavfile,
+ int numsamples,
+ void (*pfnPrint)( const char *fmt, ... ),
+ CSentence& inwords,
+ CSentence& outwords )
+{
+ LogReset();
+
+ USES_CONVERSION;
+
+ CSpDynamicString text;
+ text.Clear();
+
+ HKEY hkwipe;
+ LONG lResult = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\Speech\\RecoProfiles", 0, KEY_ALL_ACCESS, &hkwipe );
+ if ( lResult == ERROR_SUCCESS )
+ {
+ RecursiveRegDelKey( hkwipe );
+ RegCloseKey( hkwipe );
+ }
+
+ if ( strlen( inwords.GetText() ) <= 0 )
+ {
+ inwords.SetTextFromWords();
+ }
+
+ // Construct a string from the inwords array
+ text.Append( T2W( inwords.GetText() ) );
+
+ // Assume failure
+ SR_RESULT result = SR_RESULT_ERROR;
+
+ if ( text.Length() > 0 )
+ {
+ CSentence sentence;
+
+ pfnPrint( "Processing...\r\n" );
+
+ // Give it a try
+ result = ExtractPhonemes( wavfile, text, sentence, pfnPrint );
+
+ pfnPrint( "Finished.\r\n" );
+ // PrintWordsAndPhonemes( sentence, pfnPrint );
+
+ // Copy results to outputs
+ outwords.Reset();
+
+ outwords.SetText( inwords.GetText() );
+
+ Log( "Starting\n" );
+ LogWords( inwords );
+
+ if ( SR_RESULT_ERROR != result )
+ {
+ int i;
+
+ Log( "Hypothesized\n" );
+ LogWords( sentence );
+
+ for( i = 0 ; i < sentence.m_Words.Size(); i++ )
+ {
+ CWordTag *tag = sentence.m_Words[ i ];
+ if ( tag )
+ {
+ // Skip '...' tag
+ if ( stricmp( tag->GetWord(), "..." ) )
+ {
+ CWordTag *newTag = new CWordTag( *tag );
+
+ outwords.m_Words.AddToTail( newTag );
+ }
+ }
+ }
+
+ // Now insert unrecognized/skipped words from original list
+ //
+ int frompos = 0, topos = 0;
+
+ while( 1 )
+ {
+ // End of source list
+ if ( frompos >= inwords.m_Words.Size() )
+ break;
+
+ const CWordTag *fromTag = inwords.m_Words[ frompos ];
+
+ // Reached end of destination list, just copy words over from from source list until
+ // we run out of source words
+ if ( topos >= outwords.m_Words.Size() )
+ {
+ // Just copy words over
+ CWordTag *newWord = new CWordTag( *fromTag );
+
+ // Remove phonemes
+ while ( newWord->m_Phonemes.Size() > 0 )
+ {
+ CPhonemeTag *kill = newWord->m_Phonemes[ 0 ];
+ newWord->m_Phonemes.Remove( 0 );
+ delete kill;
+ }
+
+ outwords.m_Words.AddToTail( newWord );
+ frompos++;
+ topos++;
+ continue;
+ }
+
+ // Destination word
+ const CWordTag *toTag = outwords.m_Words[ topos ];
+
+ // Words match, just skip ahead
+ if ( !stricmp( fromTag->GetWord(), toTag->GetWord() ) )
+ {
+ frompos++;
+ topos++;
+ continue;
+ }
+
+ // The only case we handle is that something in the source wasn't in the destination
+
+ // Find the next source word that appears in the destination
+ int skipAhead = frompos + 1;
+ bool found = false;
+ while ( skipAhead < inwords.m_Words.Size() )
+ {
+ const CWordTag *sourceWord = inwords.m_Words[ skipAhead ];
+ if ( !stricmp( sourceWord->GetWord(), toTag->GetWord() ) )
+ {
+ found = true;
+ break;
+ }
+
+ skipAhead++;
+ }
+
+ // Uh oh destination has words that are not in source, just skip to next destination word?
+ if ( !found )
+ {
+ topos++;
+ }
+ else
+ {
+ // Copy words from from source list into destination
+ //
+ int skipCount = skipAhead - frompos;
+
+ while ( --skipCount>= 0 )
+ {
+ const CWordTag *sourceWord = inwords.m_Words[ frompos++ ];
+ CWordTag *newWord = new CWordTag( *sourceWord );
+
+ // Remove phonemes
+ while ( newWord->m_Phonemes.Size() > 0 )
+ {
+ CPhonemeTag *kill = newWord->m_Phonemes[ 0 ];
+ newWord->m_Phonemes.Remove( 0 );
+ delete kill;
+ }
+
+ outwords.m_Words.InsertBefore( topos, newWord );
+ topos++;
+ }
+
+ frompos++;
+ topos++;
+ }
+ }
+
+ Log( "\nDone simple check\n" );
+
+ LogWords( outwords );
+ LogPhonemes( outwords );
+
+ ComputeMissingByteSpans( numsamples, outwords );
+
+ Log( "\nFinal check\n" );
+
+ LogWords( outwords );
+ LogPhonemes( outwords );
+ }
+ }
+ else
+ {
+ pfnPrint( "Input sentence is empty!\n" );
+ }
+
+ // Return results
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Expose the interface
+//-----------------------------------------------------------------------------
+class CPhonemeExtractorSAPI : public IPhonemeExtractor
+{
+public:
+ virtual PE_APITYPE GetAPIType() const
+ {
+ return SPEECH_API_SAPI;
+ }
+
+ // Used for menus, etc
+ virtual char const *GetName() const
+ {
+ return "MS SAPI 5.1";
+ }
+
+ SR_RESULT Extract(
+ const char *wavfile,
+ int numsamples,
+ void (*pfnPrint)( const char *fmt, ... ),
+ CSentence& inwords,
+ CSentence& outwords )
+ {
+ return SAPI_ExtractPhonemes( wavfile, numsamples, pfnPrint, inwords, outwords );
+ }
+};
+
+EXPOSE_SINGLE_INTERFACE( CPhonemeExtractorSAPI, IPhonemeExtractor, VPHONEME_EXTRACTOR_INTERFACE );
\ No newline at end of file diff --git a/mp/src/utils/phonemeextractor/phonemeextractor_ims.cpp b/mp/src/utils/phonemeextractor/phonemeextractor_ims.cpp new file mode 100644 index 00000000..70819f8e --- /dev/null +++ b/mp/src/utils/phonemeextractor/phonemeextractor_ims.cpp @@ -0,0 +1,1075 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include <stdio.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmreg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "phonemeextractor/PhonemeExtractor.h"
+#include "ims_helper/ims_helper.h"
+
+#include "tier0/dbg.h"
+#include "sentence.h"
+#include "PhonemeConverter.h"
+#include "tier1/strtools.h"
+
+#define TEXTLESS_WORDNAME "[Textless]"
+
+static IImsHelper *talkback = NULL;
+
+//-----------------------------------------------------------------------------
+// Purpose: Expose the interface
+//-----------------------------------------------------------------------------
+class CPhonemeExtractorLipSinc : public IPhonemeExtractor
+{
+public:
+ virtual PE_APITYPE GetAPIType() const
+ {
+ return SPEECH_API_LIPSINC;
+ }
+
+ // Used for menus, etc
+ virtual char const *GetName() const
+ {
+ return "IMS (LipSinc)";
+ }
+
+ SR_RESULT Extract(
+ const char *wavfile,
+ int numsamples,
+ void (*pfnPrint)( const char *fmt, ... ),
+ CSentence& inwords,
+ CSentence& outwords );
+
+
+ CPhonemeExtractorLipSinc( void );
+ ~CPhonemeExtractorLipSinc( void );
+
+ enum
+ {
+ MAX_WORD_LENGTH = 128,
+ };
+private:
+
+
+ class CAnalyzedWord
+ {
+ public:
+ char buffer[ MAX_WORD_LENGTH ];
+ double starttime;
+ double endtime;
+ };
+
+ class CAnalyzedPhoneme
+ {
+ public:
+ char phoneme[ 32 ];
+ double starttime;
+ double endtime;
+ };
+
+ bool InitLipSinc( void );
+ void ShutdownLipSinc( void );
+
+ void DescribeError( TALKBACK_ERR err );
+ void Printf( char const *fmt, ... );
+
+ bool CheckSoundFile( char const *filename );
+ bool GetInitialized( void );
+ void SetInitialized( bool init );
+
+ void (*m_pfnPrint)( const char *fmt, ... );
+
+ char const *ConstructInputSentence( CSentence& inwords );
+ bool AttemptAnalysis( TALKBACK_ANALYSIS **ppAnalysis, char const *wavfile, CSentence& inwords );
+
+ char const *ApplyTBWordRules( char const *word );
+
+ void ProcessWords( TALKBACK_ANALYSIS *analysis, CSentence& inwords, CSentence& outwords );
+ void ProcessWordsTextless( TALKBACK_ANALYSIS *analysis, CSentence& outwords );
+
+ int GetPhonemeIndexAtWord( TALKBACK_ANALYSIS *analysis, double time, bool checkstart );
+
+ int GetPhonemeIndexAtWordStart( TALKBACK_ANALYSIS *analysis, double starttime );
+ int GetPhonemeIndexAtWordEnd( TALKBACK_ANALYSIS *analysis, double endtime );
+
+ CAnalyzedWord *GetAnalyzedWord( TALKBACK_ANALYSIS *analysis, int index );
+ CAnalyzedPhoneme *GetAnalyzedPhoneme( TALKBACK_ANALYSIS *analysis, int index );
+
+ int ComputeByteFromTime( float time );
+
+ bool m_bInitialized;
+
+ float m_flSampleCount;
+ float m_flDuration;
+
+ float m_flSamplesPerSecond;
+
+ int m_nBytesPerSample;
+
+ HMODULE m_hHelper;
+};
+
+CPhonemeExtractorLipSinc::CPhonemeExtractorLipSinc( void )
+{
+ m_hHelper = (HMODULE)0;
+ m_pfnPrint = NULL;
+
+ m_bInitialized = false;
+
+ m_flSampleCount = 0.0f;
+ m_flDuration = 0.0f;
+
+ m_flSamplesPerSecond = 0.0f;
+
+ m_nBytesPerSample = 0;
+}
+
+CPhonemeExtractorLipSinc::~CPhonemeExtractorLipSinc( void )
+{
+ if ( GetInitialized() )
+ {
+ ShutdownLipSinc();
+ }
+}
+
+bool CPhonemeExtractorLipSinc::GetInitialized( void )
+{
+ return m_bInitialized;
+}
+
+void CPhonemeExtractorLipSinc::SetInitialized( bool init )
+{
+ m_bInitialized = init;
+}
+
+int CPhonemeExtractorLipSinc::ComputeByteFromTime( float time )
+{
+ if ( !m_flDuration )
+ return 0;
+
+ float frac = time / m_flDuration;
+
+ float sampleNumber = frac * m_flSampleCount;
+
+ int bytenumber = sampleNumber * m_nBytesPerSample;
+
+ return bytenumber;
+}
+
+void CPhonemeExtractorLipSinc::DescribeError( TALKBACK_ERR err )
+{
+ Assert( m_pfnPrint );
+
+ // Get the error description.
+ char errorDesc[256] = "";
+ if ( err != TALKBACK_NOERR )
+ {
+ talkback->TalkBackGetErrorString( err, sizeof(errorDesc), errorDesc );
+ }
+
+ // Report or log the error...
+ (*m_pfnPrint)( "LIPSINC ERROR: %s\n", errorDesc );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *fmt -
+// .. -
+//-----------------------------------------------------------------------------
+void CPhonemeExtractorLipSinc::Printf( char const *fmt, ... )
+{
+ Assert( m_pfnPrint );
+
+ char string[ 4096 ];
+
+ va_list argptr;
+ va_start( argptr, fmt );
+ vsprintf( string, fmt, argptr );
+ va_end( argptr );
+
+ (*m_pfnPrint)( "%s", string );
+}
+
+bool CPhonemeExtractorLipSinc::CheckSoundFile( char const *filename )
+{
+ TALKBACK_SOUND_FILE_METRICS fm;
+ memset( &fm, 0, sizeof( fm ) );
+ fm.m_size = sizeof( fm );
+
+ TALKBACK_ERR err = talkback->TalkBackGetSoundFileMetrics( filename, &fm );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return false;
+ }
+
+ if ( fm.m_canBeAnalyzed )
+ {
+ Printf( "%s: %.2f s, rate %i, bits %i, channels %i\n",
+ filename,
+ fm.m_duration,
+ fm.m_sampleRate,
+ fm.m_bitsPerSample,
+ fm.m_channelCount );
+ }
+
+ m_flDuration = fm.m_duration;
+ if ( m_flDuration > 0 )
+ {
+ m_flSamplesPerSecond = m_flSampleCount / m_flDuration;
+ }
+ else
+ {
+ m_flSamplesPerSecond = 0.0f;
+ }
+
+ m_nBytesPerSample = ( fm.m_bitsPerSample >> 3 );
+
+ m_flSampleCount /= m_nBytesPerSample;
+
+ m_nBytesPerSample /= fm.m_channelCount;
+
+ return fm.m_canBeAnalyzed ? true : false;
+}
+
+typedef IImsHelper *(*pfnImsHelper)(void);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPhonemeExtractorLipSinc::InitLipSinc( void )
+{
+ if ( GetInitialized() )
+ {
+ return true;
+ }
+
+ m_hHelper = LoadLibrary( "ims_helper.dll" );
+ if ( !m_hHelper )
+ {
+ return false;
+ }
+
+ pfnImsHelper factory = (pfnImsHelper)::GetProcAddress( m_hHelper, "GetImsHelper" );
+ if ( !factory )
+ {
+ FreeLibrary( m_hHelper );
+ return false;
+ }
+
+ talkback = reinterpret_cast< IImsHelper * >( (*factory)() );
+ if ( !talkback )
+ {
+ FreeLibrary( m_hHelper );
+ return false;
+ }
+
+ char szExeName[ MAX_PATH ];
+ szExeName[0] = 0;
+ GetModuleFileName( (HMODULE)0, szExeName, sizeof( szExeName ) );
+
+ char szBaseDir[ MAX_PATH ];
+ Q_strncpy( szBaseDir, szExeName, sizeof( szBaseDir ) );
+
+ Q_StripLastDir( szBaseDir, sizeof( szBaseDir ) );
+ Q_StripTrailingSlash( szBaseDir );
+ Q_strlower( szBaseDir );
+
+ char coreDataDir[ 512 ];
+ Q_snprintf( coreDataDir, sizeof( coreDataDir ), "%s\\lipsinc_data\\",
+ szBaseDir );
+ Q_FixSlashes( coreDataDir );
+
+ char szCheck[ 512 ];
+ Q_snprintf( szCheck, sizeof( szCheck ), "%sDtC6dal.dat", coreDataDir );
+ struct __stat64 buf;
+
+ if ( _stat64( szCheck, &buf ) != 0 )
+ {
+ Q_snprintf( coreDataDir, sizeof( coreDataDir ), "%s\\bin\\lipsinc_data\\",
+ szBaseDir );
+ Q_FixSlashes( coreDataDir );
+ Q_snprintf( szCheck, sizeof( szCheck ), "%sDtC6dal.dat", coreDataDir );
+
+ if ( _stat64( szCheck, &buf ) != 0 )
+ {
+ Error( "Unable to find talkback data files in %s.", coreDataDir );
+ }
+ }
+
+ TALKBACK_ERR err;
+
+ err = talkback->TalkBackStartupLibrary( coreDataDir );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ FreeLibrary( m_hHelper );
+ return false;
+ }
+
+ long verMajor = 0;
+ long verMinor = 0;
+ long verRevision = 0;
+
+ err = talkback->TalkBackGetVersion(
+ &verMajor,
+ &verMinor,
+ &verRevision);
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ FreeLibrary( m_hHelper );
+ return false;
+ }
+
+ Printf( "Lipsinc TalkBack Version %i.%i.%i\n", verMajor, verMinor, verRevision );
+
+ m_bInitialized = true;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPhonemeExtractorLipSinc::ShutdownLipSinc( void )
+{
+ // HACK HACK: This seems to crash on exit sometimes
+ __try
+ {
+ talkback->TalkBackShutdownLibrary();
+
+ FreeLibrary( m_hHelper );
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER )
+ {
+ OutputDebugString( "----> Crash shutting down TALKBACK sdk, exception caught and ignored\n" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : inwords -
+// Output : char const
+//-----------------------------------------------------------------------------
+char const *CPhonemeExtractorLipSinc::ConstructInputSentence( CSentence& inwords )
+{
+ static char sentence[ 16384 ];
+
+ sentence[ 0 ] = 0;
+
+ int last = inwords.m_Words.Size() - 1;
+
+ for ( int i = 0 ; i <= last; i++ )
+ {
+ CWordTag *w = inwords.m_Words[ i ];
+
+ strcat( sentence, w->GetWord() );
+ if ( i != last )
+ {
+ strcat( sentence, " " );
+ }
+ }
+
+ if ( inwords.m_Words.Count() == 1 &&
+ !Q_strnicmp( inwords.GetText(), TEXTLESS_WORDNAME, Q_strlen( TEXTLESS_WORDNAME ) ) )
+ {
+ sentence[ 0 ] = 0;
+ }
+
+ return sentence;
+}
+
+bool CPhonemeExtractorLipSinc::AttemptAnalysis( TALKBACK_ANALYSIS **ppAnalysis, char const *wavfile, CSentence& inwords )
+{
+ *ppAnalysis = NULL;
+
+ TALKBACK_ANALYSIS_SETTINGS settings;
+ memset( &settings, 0, sizeof( settings ) );
+
+ // Set this field to sizeof(TALKBACK_ANALYSIS_SETTINGS) before using the
+ // structure.
+ settings.fSize = sizeof( TALKBACK_ANALYSIS_SETTINGS );
+
+
+ // Default value: 30 (frames per second).
+ settings.fFrameRate = 100;
+ // Set this to 1 to optimize for flipbook output, 0 to do analysis normally.
+ //
+ // Default value: 0 (normal analysis).
+ settings.fOptimizeForFlipbook = 0;
+ // Set this to -1 to seed the random number generator with the current time.
+ // Any other number will be used directly for the random number seed, which
+ // is useful if you want repeatable speech gestures. This value does not
+ // influence lip-synching at all.
+ //
+ // Default value: -1 (use current time).
+ settings.fRandomSeed = -1;
+ // Path to the configuration (.INI) file with phoneme-to-speech-target
+ // mapping. Set this to NULL to use the default mapping.
+ //
+ // Default value: NULL (use default mapping).
+ settings.fConfigFile = NULL;
+
+ char const *text = ConstructInputSentence( inwords );
+
+ Printf( "Analyzing: \"%s\"\n", text[ 0 ] ? text : TEXTLESS_WORDNAME );
+
+ TALKBACK_ERR err = talkback->TalkBackGetAnalysis(
+ ppAnalysis,
+ wavfile,
+ text,
+ &settings );
+
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return false;
+ }
+
+ Printf( "Analysis successful...\n" );
+
+ return true;
+}
+
+typedef struct
+{
+ TALKBACK_PHONEME phoneme;
+ char const *string;
+} TBPHONEMES_t;
+
+static TBPHONEMES_t g_TBPhonemeList[]=
+{
+ { TALKBACK_PHONEME_IY, "iy" },
+ { TALKBACK_PHONEME_IH, "ih" },
+ { TALKBACK_PHONEME_EH, "eh" },
+ { TALKBACK_PHONEME_EY, "ey" },
+ { TALKBACK_PHONEME_AE, "ae" },
+ { TALKBACK_PHONEME_AA, "aa" },
+ { TALKBACK_PHONEME_AW, "aw" },
+ { TALKBACK_PHONEME_AY, "ay" },
+ { TALKBACK_PHONEME_AH, "ah" },
+ { TALKBACK_PHONEME_AO, "ao" },
+ { TALKBACK_PHONEME_OY, "oy" },
+ { TALKBACK_PHONEME_OW, "ow" },
+ { TALKBACK_PHONEME_UH, "uh" },
+ { TALKBACK_PHONEME_UW, "uw" },
+ { TALKBACK_PHONEME_ER, "er" },
+ { TALKBACK_PHONEME_AX, "ax" },
+ { TALKBACK_PHONEME_S, "s" },
+ { TALKBACK_PHONEME_SH, "sh" },
+ { TALKBACK_PHONEME_Z, "z" },
+ { TALKBACK_PHONEME_ZH, "zh" },
+ { TALKBACK_PHONEME_F, "f" },
+ { TALKBACK_PHONEME_TH, "th" },
+ { TALKBACK_PHONEME_V, "v" },
+ { TALKBACK_PHONEME_DH, "dh" },
+ { TALKBACK_PHONEME_M, "m" },
+ { TALKBACK_PHONEME_N, "n" },
+ { TALKBACK_PHONEME_NG, "ng" },
+ { TALKBACK_PHONEME_L, "l" },
+ { TALKBACK_PHONEME_R, "r" },
+ { TALKBACK_PHONEME_W, "w" },
+ { TALKBACK_PHONEME_Y, "y" },
+ { TALKBACK_PHONEME_HH, "hh" },
+ { TALKBACK_PHONEME_B, "b" },
+ { TALKBACK_PHONEME_D, "d" },
+ { TALKBACK_PHONEME_JH, "jh" },
+ { TALKBACK_PHONEME_G, "g" },
+ { TALKBACK_PHONEME_P, "p" },
+ { TALKBACK_PHONEME_T, "t" },
+ { TALKBACK_PHONEME_K, "k" },
+ { TALKBACK_PHONEME_CH, "ch" },
+ { TALKBACK_PHONEME_SIL, "<sil>" },
+ { -1, NULL }
+};
+
+char const *TBPhonemeToString( TALKBACK_PHONEME phoneme )
+{
+ if ( phoneme < TALKBACK_PHONEME_FIRST || phoneme > TALKBACK_PHONEME_LAST )
+ {
+ return "Bogus";
+ }
+
+ TBPHONEMES_t *item = &g_TBPhonemeList[ phoneme ];
+ return item->string;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *analysis -
+// time -
+// start -
+// Output : int
+//-----------------------------------------------------------------------------
+int CPhonemeExtractorLipSinc::GetPhonemeIndexAtWord( TALKBACK_ANALYSIS *analysis, double time, bool start )
+{
+ long count;
+
+ TALKBACK_ERR err = talkback->TalkBackGetNumPhonemes( analysis, &count );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return -1;
+ }
+
+ if ( count <= 0L )
+ return -1;
+
+ // Bogus
+ if ( count >= 100000L )
+ return -1;
+
+ for ( int i = 0; i < (int)count; i++ )
+ {
+ TALKBACK_PHONEME tbPhoneme = TALKBACK_PHONEME_INVALID;
+ err = talkback->TalkBackGetPhonemeEnum( analysis, i, &tbPhoneme );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ continue;
+ }
+
+ double t;
+
+ if ( start )
+ {
+ err = talkback->TalkBackGetPhonemeStartTime( analysis, i, &t );
+ }
+ else
+ {
+ err = talkback->TalkBackGetPhonemeEndTime( analysis, i, &t );
+ }
+
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ continue;
+ }
+
+ if ( t == time )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *analysis -
+// starttime -
+// Output : int
+//-----------------------------------------------------------------------------
+int CPhonemeExtractorLipSinc::GetPhonemeIndexAtWordStart( TALKBACK_ANALYSIS *analysis, double starttime )
+{
+ return GetPhonemeIndexAtWord( analysis, starttime, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *analysis -
+// endtime -
+// Output : int
+//-----------------------------------------------------------------------------
+int CPhonemeExtractorLipSinc::GetPhonemeIndexAtWordEnd( TALKBACK_ANALYSIS *analysis, double endtime )
+{
+ return GetPhonemeIndexAtWord( analysis, endtime, false );
+}
+
+CPhonemeExtractorLipSinc::CAnalyzedPhoneme *CPhonemeExtractorLipSinc::GetAnalyzedPhoneme( TALKBACK_ANALYSIS *analysis, int index )
+{
+ static CAnalyzedPhoneme p;
+
+ memset( &p, 0, sizeof( p ) );
+
+ TALKBACK_PHONEME tb;
+
+ TALKBACK_ERR err = talkback->TalkBackGetPhonemeEnum( analysis, index, &tb );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return NULL;
+ }
+
+ strcpy( p.phoneme, TBPhonemeToString( tb ) );
+
+ err = talkback->TalkBackGetPhonemeStartTime( analysis, index, &p.starttime );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return NULL;
+ }
+ err = talkback->TalkBackGetPhonemeEndTime( analysis, index, &p.endtime );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return NULL;
+ }
+
+ return &p;
+}
+
+CPhonemeExtractorLipSinc::CAnalyzedWord *CPhonemeExtractorLipSinc::GetAnalyzedWord( TALKBACK_ANALYSIS *analysis, int index )
+{
+ static CAnalyzedWord w;
+
+ memset( &w, 0, sizeof( w ) );
+
+ long chars = sizeof( w.buffer );
+
+ TALKBACK_ERR err = talkback->TalkBackGetWord( analysis, index, chars, w.buffer );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return NULL;
+ }
+
+ err = talkback->TalkBackGetWordStartTime( analysis, index, &w.starttime );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return NULL;
+ }
+ err = talkback->TalkBackGetWordEndTime( analysis, index, &w.endtime );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return NULL;
+ }
+
+ return &w;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *w1 -
+// *w2 -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool FuzzyWordMatch( char const *w1, char const *w2 )
+{
+ int len1 = strlen( w1 );
+ int len2 = strlen( w2 );
+
+ int minlen = min( len1, len2 );
+
+ // Found a match
+ if ( !strnicmp( w1, w2, minlen ) )
+ return true;
+
+ int letterdiff = abs( len1 - len2 );
+ // More than three letters different, don't bother
+ if ( letterdiff > 5 )
+ return false;
+
+ // Compute a "delta"
+ char *p1 = (char *)w1;
+ char *p2 = (char *)w2;
+
+ CUtlVector <char> word1;
+ CUtlVector <char> word2;
+
+ while ( *p1 )
+ {
+ if ( V_isalpha( *p1 ) )
+ {
+ word1.AddToTail( *p1 );
+ }
+ p1++;
+ }
+
+ while ( *p2 )
+ {
+ if ( V_isalpha( *p2 ) )
+ {
+ word2.AddToTail( *p2 );
+ }
+ p2++;
+ }
+
+ int i;
+ for ( i = 0; i < word1.Size(); i++ )
+ {
+ char c = word1[ i ];
+
+ // See if c is in word 2, if so subtract it out
+ int idx = word2.Find( c );
+
+ if ( idx != word2.InvalidIndex() )
+ {
+ word2.Remove( idx );
+ }
+ }
+
+ if ( word2.Size() <= letterdiff )
+ return true;
+
+ word2.RemoveAll();
+
+ while ( *p2 )
+ {
+ if ( V_isalpha( *p2 ) )
+ {
+ word2.AddToTail( *p2 );
+ }
+ p2++;
+ }
+
+ for ( i = 0; i < word2.Size(); i++ )
+ {
+ char c = word2[ i ];
+
+ // See if c is in word 2, if so subtract it out
+ int idx = word1.Find( c );
+
+ if ( idx != word1.InvalidIndex() )
+ {
+ word1.Remove( idx );
+ }
+ }
+
+ if ( word1.Size() <= letterdiff )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For foreign language stuff, if inwords is empty, process anyway...
+// Input : *analysis -
+// outwords -
+//-----------------------------------------------------------------------------
+void CPhonemeExtractorLipSinc::ProcessWordsTextless( TALKBACK_ANALYSIS *analysis, CSentence& outwords )
+{
+ long count;
+
+ TALKBACK_ERR err = talkback->TalkBackGetNumPhonemes( analysis, &count );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return;
+ }
+
+ CWordTag *newWord = new CWordTag;
+
+ newWord->SetWord( TEXTLESS_WORDNAME );
+
+ float starttime = 0.0f;
+ float endtime = 1.0f;
+
+
+ for ( int i = 0; i < count; ++i )
+ {
+ // Get phoneme and timing info
+ CAnalyzedPhoneme *ph = GetAnalyzedPhoneme( analysis, i );
+ if ( !ph )
+ continue;
+
+ CPhonemeTag *ptag = new CPhonemeTag;
+
+ if ( i == 0 || ( ph->starttime < starttime ) )
+ {
+ starttime = ph->starttime;
+ }
+
+ if ( i == 0 || ( ph->endtime > endtime ) )
+ {
+ endtime = ph->endtime;
+ }
+
+ ptag->SetStartTime( ph->starttime );
+ ptag->SetEndTime( ph->endtime );
+
+ ptag->m_uiStartByte = ComputeByteFromTime( ph->starttime );
+ ptag->m_uiEndByte = ComputeByteFromTime( ph->endtime );
+
+ ptag->SetTag( ph->phoneme );
+ ptag->SetPhonemeCode( TextToPhoneme( ptag->GetTag() ) );
+
+ newWord->m_Phonemes.AddToTail( ptag );
+ }
+
+ newWord->m_flStartTime = starttime;
+ newWord->m_flEndTime = endtime;
+
+ newWord->m_uiStartByte = ComputeByteFromTime( starttime );
+ newWord->m_uiEndByte = ComputeByteFromTime( endtime );
+
+ outwords.Reset();
+ outwords.AddWordTag( newWord );
+ outwords.SetTextFromWords();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *analysis -
+// inwords -
+// outwords -
+//-----------------------------------------------------------------------------
+void CPhonemeExtractorLipSinc::ProcessWords( TALKBACK_ANALYSIS *analysis, CSentence& inwords, CSentence& outwords )
+{
+ long count;
+
+ TALKBACK_ERR err = talkback->TalkBackGetNumWords( analysis, &count );
+ if ( err != TALKBACK_NOERR )
+ {
+ DescribeError( err );
+ return;
+ }
+
+ if ( count <= 0L )
+ {
+ if ( inwords.m_Words.Count() == 0 ||
+ !Q_strnicmp( inwords.GetText(), TEXTLESS_WORDNAME, Q_strlen( TEXTLESS_WORDNAME ) ) )
+ {
+ ProcessWordsTextless( analysis, outwords );
+ }
+ return;
+ }
+
+ // Bogus
+ if ( count >= 100000L )
+ return;
+
+ int inwordpos = 0;
+ int awordpos = 0;
+
+ outwords.Reset();
+
+ char previous[ 256 ];
+ previous[ 0 ] = 0;
+
+ while ( inwordpos < inwords.m_Words.Size() )
+ {
+ CWordTag *in = inwords.m_Words[ inwordpos ];
+
+ if ( awordpos >= count )
+ {
+ // Just copy the rest over without phonemes
+ CWordTag *copy = new CWordTag( *in );
+
+ outwords.AddWordTag( copy );
+
+ inwordpos++;
+ continue;
+ }
+
+ // Should never fail
+ CAnalyzedWord *w = GetAnalyzedWord( analysis, awordpos );
+ if ( !w )
+ {
+ return;
+ }
+
+ if ( !stricmp( w->buffer, "<SIL>" ) )
+ {
+ awordpos++;
+ continue;
+ }
+
+ char const *check = ApplyTBWordRules( in->GetWord() );
+ if ( !FuzzyWordMatch( check, w->buffer ) )
+ {
+ bool advance_input = true;
+ if ( previous[ 0 ] )
+ {
+ if ( FuzzyWordMatch( previous, w->buffer ) )
+ {
+ advance_input = false;
+ }
+ }
+
+ if ( advance_input )
+ {
+ inwordpos++;
+ }
+ awordpos++;
+ continue;
+ }
+ strcpy( previous, check );
+
+ CWordTag *newWord = new CWordTag;
+
+ newWord->SetWord( in->GetWord() );
+
+ newWord->m_flStartTime = w->starttime;
+ newWord->m_flEndTime = w->endtime;
+
+ newWord->m_uiStartByte = ComputeByteFromTime( w->starttime );
+ newWord->m_uiEndByte = ComputeByteFromTime( w->endtime );
+
+ int phonemestart, phonemeend;
+
+ phonemestart = GetPhonemeIndexAtWordStart( analysis, w->starttime );
+ phonemeend = GetPhonemeIndexAtWordEnd( analysis, w->endtime );
+
+ if ( phonemestart >= 0 && phonemeend >= 0 )
+ {
+ for ( ; phonemestart <= phonemeend; phonemestart++ )
+ {
+ // Get phoneme and timing info
+ CAnalyzedPhoneme *ph = GetAnalyzedPhoneme( analysis, phonemestart );
+ if ( !ph )
+ continue;
+
+ CPhonemeTag *ptag = new CPhonemeTag;
+ ptag->SetStartTime( ph->starttime );
+ ptag->SetEndTime( ph->endtime );
+
+ ptag->m_uiStartByte = ComputeByteFromTime( ph->starttime );
+ ptag->m_uiEndByte = ComputeByteFromTime( ph->endtime );
+
+ ptag->SetTag( ph->phoneme );
+ ptag->SetPhonemeCode( TextToPhoneme( ptag->GetTag() ) );
+
+ newWord->m_Phonemes.AddToTail( ptag );
+ }
+ }
+
+ outwords.AddWordTag( newWord );
+ inwordpos++;
+ awordpos++;
+ }
+}
+
+char const *CPhonemeExtractorLipSinc::ApplyTBWordRules( char const *word )
+{
+ static char outword[ 256 ];
+
+ char const *in = word;
+ char *out = outword;
+
+ while ( *in && ( ( out - outword ) <= 255 ) )
+ {
+ if ( *in == '\t' ||
+ *in == ' ' ||
+ *in == '\n' ||
+ *in == '-' ||
+ *in == '.' ||
+ *in == ',' ||
+ *in == ';' ||
+ *in == '?' ||
+ *in == '"' ||
+ *in == ':' ||
+ *in == '(' ||
+ *in == ')' )
+ {
+ in++;
+ *out++ = ' ';
+ continue;
+ }
+
+ if ( !V_isprint( *in ) )
+ {
+ in++;
+ continue;
+ }
+
+ if ( *in >= 128 )
+ {
+ in++;
+ continue;
+ }
+
+ // Skip numbers
+ if ( *in >= '0' && *in <= '9' )
+ {
+ in++;
+ continue;
+ }
+
+ // Convert all letters to upper case
+ if ( *in >= 'a' && *in <= 'z' )
+ {
+ *out++ = ( *in++ ) - 'a' + 'A';
+ continue;
+ }
+
+ if ( *in >= 'A' && *in <= 'Z' )
+ {
+ *out++ = *in++;
+ continue;
+ }
+
+ if ( *in == '\'' )
+ {
+ *out++ = *in++;
+ continue;
+ }
+
+ in++;
+ }
+
+ *out = 0;
+
+ return outword;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a wavfile and a list of inwords, determines the word/phonene
+// sample counts for the sentce
+// Output : SR_RESULT
+//-----------------------------------------------------------------------------
+SR_RESULT CPhonemeExtractorLipSinc::Extract(
+ const char *wavfile,
+ int numsamples,
+ void (*pfnPrint)( const char *fmt, ... ),
+ CSentence& inwords,
+ CSentence& outwords )
+{
+ // g_enableTalkBackDebuggingOutput = 1;
+
+ m_pfnPrint = pfnPrint;
+
+ if ( !InitLipSinc() )
+ {
+ return SR_RESULT_ERROR;
+ }
+
+ m_flSampleCount = numsamples;
+
+ if ( !CheckSoundFile( wavfile ) )
+ {
+ FreeLibrary( m_hHelper );
+ return SR_RESULT_ERROR;
+ }
+
+ TALKBACK_ANALYSIS *analysis = NULL;
+
+ if ( !AttemptAnalysis( &analysis, wavfile, inwords ) )
+ {
+ FreeLibrary( m_hHelper );
+ return SR_RESULT_FAILED;
+ }
+
+ if ( strlen( inwords.GetText() ) <= 0 )
+ {
+ inwords.SetTextFromWords();
+ }
+
+ outwords = inwords;
+
+ // Examine data
+ ProcessWords( analysis, inwords, outwords );
+
+ if ( analysis )
+ {
+ talkback->TalkBackFreeAnalysis( &analysis );
+ }
+
+ return SR_RESULT_SUCCESS;
+}
+
+EXPOSE_SINGLE_INTERFACE( CPhonemeExtractorLipSinc, IPhonemeExtractor, VPHONEME_EXTRACTOR_INTERFACE );
\ No newline at end of file diff --git a/mp/src/utils/phonemeextractor/talkback.doc b/mp/src/utils/phonemeextractor/talkback.doc Binary files differnew file mode 100644 index 00000000..8217b79a --- /dev/null +++ b/mp/src/utils/phonemeextractor/talkback.doc diff --git a/mp/src/utils/phonemeextractor/talkback.h b/mp/src/utils/phonemeextractor/talkback.h new file mode 100644 index 00000000..3a1b179a --- /dev/null +++ b/mp/src/utils/phonemeextractor/talkback.h @@ -0,0 +1,732 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// =============================================================================
+// Interface to the LIPSinc TalkBack 1.1 library (TalkBack_*.lib).
+//
+// Copyright � 1998-2002 LIPSinc. All rights reserved.
+
+#if !defined(TalkBack_h)
+#define TalkBack_h
+
+#include <stddef.h> // size_t.
+
+// Enforce a C API.
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+// -----------------------------------------------------------------------------
+// Use the preprocessor to make the new API compatible with the old one.
+
+#define TalkbackStartupLibrary TalkBackStartupLibrary
+#define TalkbackShutdownLibrary TalkBackShutdownLibrary
+#define TalkbackGetVersion TalkBackGetVersion
+#define TalkbackGetVersionString TalkBackGetVersionString
+#define TalkbackCheckSoundFile TalkBackCheckSoundFile
+#define TalkbackCheckSpokenText TalkBackCheckSpokenText
+#define TalkbackGetErrorString TalkBackGetErrorString
+#define TalkbackGetAnalysis TalkBackGetAnalysis
+#define TalkbackFreeAnalysis TalkBackFreeAnalysis
+#define TalkbackGetFirstFrameNum TalkBackGetFirstFrameNum
+#define TalkbackGetLastFrameNum TalkBackGetLastFrameNum
+#define TalkbackGetFrameStartTime TalkBackGetFrameStartTime
+#define TalkbackGetFrameEndTime TalkBackGetFrameEndTime
+#define TalkbackGetNumPhonemes TalkBackGetNumPhonemes
+#define TalkbackGetPhonemeEnum TalkBackGetPhonemeEnum
+#define TalkbackGetPhonemeStartTime TalkBackGetPhonemeStartTime
+#define TalkbackGetPhonemeEndTime TalkBackGetPhonemeEndTime
+#define TalkbackInsertPhoneme TalkBackInsertPhoneme
+#define TalkbackDeletePhoneme TalkBackDeletePhoneme
+#define TalkbackChangePhonemeStart TalkBackChangePhonemeStart
+#define TalkbackChangePhonemeEnd TalkBackChangePhonemeEnd
+#define TalkbackChangePhonemeEnum TalkBackChangePhonemeEnum
+#define TalkbackGetNumWords TalkBackGetNumWords
+#define TalkbackGetWord TalkBackGetWord
+#define TalkbackGetWordStartTime TalkBackGetWordStartTime
+#define TalkbackGetWordEndTime TalkBackGetWordEndTime
+#define TalkbackGetNumSpeechTargetTracks TalkBackGetNumSpeechTargetTracks
+#define TalkbackGetNumSpeechTargetKeys TalkBackGetNumSpeechTargetKeys
+#define TalkbackGetSpeechTargetKeyInfo TalkBackGetSpeechTargetKeyInfo
+#define TalkbackGetSpeechTargetValueAtFrame TalkBackGetSpeechTargetValueAtFrame
+#define TalkbackGetDominantSpeechTargetAtFrame TalkBackGetDominantSpeechTargetAtFrame
+#define TalkbackGetSpeechTargetValueAtTime TalkBackGetSpeechTargetValueAtTime
+#define TalkbackGetSpeechTargetDerivativesAtTime TalkBackGetSpeechTargetDerivativesAtTime
+#define TalkbackGetNumGestureTracks TalkBackGetNumGestureTracks
+#define TalkbackGetNumGestureKeys TalkBackGetNumGestureKeys
+#define TalkbackGetGestureKeyInfo TalkBackGetGestureKeyInfo
+#define TalkbackGetGestureValueAtFrame TalkBackGetGestureValueAtFrame
+#define TalkbackGetGestureValueAtTime TalkBackGetGestureValueAtTime
+#define TalkbackGetGestureDerivativesAtTime TalkBackGetGestureDerivativesAtTime
+
+// -----------------------------------------------------------------------------
+// For debug builds, set this to a non-zero value to get verbose debugging
+// output from TalkBack.
+
+extern int g_enableTalkBackDebuggingOutput;
+
+// -----------------------------------------------------------------------------
+// Miscellaneous constants.
+
+// For calling TalkBackGetAnalysis() with all defaults.
+#define TALKBACK_DEFAULT_SETTINGS NULL
+
+// For setting the iSoundText parameter in TalkBackGetAnalysis() to "no text."
+#define TALKBACK_NO_TEXT NULL
+
+// Handy constants for TALKBACK_ANALYSIS_SETTINGS fields:
+
+ // For setting fSize.
+#define TALKBACK_SETTINGS_SIZE sizeof(TALKBACK_ANALYSIS_SETTINGS)
+ // For setting fFrameRate to the
+ // default.
+#define TALKBACK_DEFAULT_FRAME_RATE 30
+ // For setting fOptimizeForFlipbook
+ // to *not* optimize for flipbook.
+#define TALKBACK_OPTIMIZE_FOR_FLIPBOOK_OFF 0
+ // For setting fOptimizeForFlipbook
+ // to optimize for flipbook.
+#define TALKBACK_OPTIMIZE_FOR_FLIPBOOK_ON 1
+ // For setting fRandomSeed to use the
+ // current time to seed the random
+ // number generator and thereby get
+ // non-deterministic speech gestures.
+#define TALKBACK_RANDOM_SEED -1
+ // For setting fConfigFile to "no
+ // config file."
+#define TALKBACK_NO_CONFIG_FILE NULL
+
+// -----------------------------------------------------------------------------
+// Data types.
+
+// TALKBACK_NOERR if successful, TalkBack error code if not.
+typedef long TALKBACK_ERR;
+
+// Opaque analysis results.
+typedef void TALKBACK_ANALYSIS;
+
+// Speech target.
+typedef long TALKBACK_SPEECH_TARGET;
+
+// Speech gesture.
+typedef long TALKBACK_GESTURE;
+
+// Phoneme.
+typedef long TALKBACK_PHONEME;
+
+// -----------------------------------------------------------------------------
+// Data structures.
+
+#pragma pack(push, 1)
+
+// Optional analysis settings passed to TalkBackGetAnalysis().
+typedef struct
+{
+ // Set this field to sizeof(TALKBACK_ANALYSIS_SETTINGS) before using the
+ // structure.
+ long fSize;
+ // Frame rate for analysis. This only matters if you will be using *AtFrame
+ // functions.
+ //
+ // Default value: 30 (frames per second).
+ long fFrameRate;
+ // Set this to 1 to optimize for flipbook output, 0 to do analysis normally.
+ //
+ // Default value: 0 (normal analysis).
+ long fOptimizeForFlipbook;
+ // Set this to -1 to seed the random number generator with the current time.
+ // Any other number will be used directly for the random number seed, which
+ // is useful if you want repeatable speech gestures. This value does not
+ // influence lip-synching at all.
+ //
+ // Default value: -1 (use current time).
+ long fRandomSeed;
+ // Path to the configuration (.INI) file with phoneme-to-speech-target
+ // mapping. Set this to NULL to use the default mapping.
+ //
+ // Default value: NULL (use default mapping).
+ char const *fConfigFile;
+} TALKBACK_ANALYSIS_SETTINGS;
+
+typedef struct
+{
+ // Set this field to sizeof(TALKBACK_SOUND_FILE_METRICS) before using the
+ // structure. This will allow the structure to evolve if necessary.
+ size_t m_size;
+ // Bits per sample.
+ long m_bitsPerSample;
+ // Sample rate in Hertz.
+ long m_sampleRate;
+ // Duration of the audio in seconds.
+ double m_duration;
+ // 1 if the sound file can be analyzed, 0 if not.
+ long m_canBeAnalyzed;
+ // 1 if the sound file is clipped, 0 if not.
+ long m_isClipped;
+ // The decibel range of the sound file.
+ double m_decibelRange;
+ // A quality value for the sound file: the nominal range is 0 to 100. Try
+ // to keep it above 45 for good results.
+ int m_quality;
+
+ // Added for version 2 of the metrics structure:
+ // ---------------------------------------------
+ // The number of channels in the sound file: 1 for mono, 2 for stereo, etc.
+ long m_channelCount;
+} TALKBACK_SOUND_FILE_METRICS;
+
+#pragma pack(pop)
+
+// -----------------------------------------------------------------------------
+// Constants.
+
+// TalkBack error codes. Use TalkBackGetErrorString() to return text
+// descriptions for these codes.
+enum
+{
+ // Windows convention: set this bit to indicate an application-defined error
+ // code.
+ BIT29 = (1 << 29),
+ // Success (not an error).
+ TALKBACK_NOERR = 0,
+ // The first error code: useful for iterating through the error codes.
+ TALKBACK_ERROR_FIRST = 4201 | BIT29,
+ // Generic error.
+ TALKBACK_ERROR = TALKBACK_ERROR_FIRST,
+ // TalkBackStartupLibrary() failed [internal error] or was never called.
+ TALKBACK_STARTUP_FAILED_ERR,
+ // TalkBackShutdownLibrary() failed, either because
+ // TalkBackStartupLibrary() was never called or because
+ // TalkBackShutdownLibrary() has already been called.
+ TALKBACK_SHUTDOWN_FAILED_ERR,
+ // The TalkBack data files could not be found [invalid path or missing
+ // files].
+ TALKBACK_CORE_DATA_NOT_FOUND_ERR,
+ // One or more of the parameters are NULL.
+ TALKBACK_NULL_PARAMETER_ERR,
+ // One or more of the parameters is invalid.
+ TALKBACK_INVALID_PARAMETER_ERR,
+ // The analysis object pointer is invalid.
+ TALKBACK_INVALID_ANALYSIS_ERR,
+ // Analysis failed [the sound file cannot be analyzed or an internal error
+ // occurred].
+ TALKBACK_ANALYSIS_FAILED_ERR,
+ // One or more of the indices (track, key, frame, word, phoneme) are
+ // invalid (out of range).
+ TALKBACK_INVALID_INDEX_ERR,
+ // The time parameter is invalid (out of range).
+ TALKBACK_INVALID_TIME_ERR,
+ // A serious internal error occurred in TalkBack; please alert LIPSinc by
+ // sending mail with a description of how the error was triggered to
+ // [email protected].
+ TALKBACK_INTERNAL_ERR,
+ // Could not open the specified sound file.
+ TALKBACK_COULD_NOT_LOAD_SOUND_ERR,
+ // TalkBackStartupLibrary() has not been called.
+ TALKBACK_STARTUP_NOT_CALLED,
+ // The configuration file specified in the TALKBACK_ANALYSIS_SETTINGS
+ // structure is invalid.
+ TALKBACK_CONFIG_PARSE_ERROR,
+ // The last error code: useful for iterating through the error codes.
+ TALKBACK_ERROR_LAST = TALKBACK_CONFIG_PARSE_ERROR
+};
+
+// Default lip-synching track identifiers.
+//
+// NOTE: these track identifiers apply *only* to the default phoneme-to-track
+// mapping! Consult the TalkBack Reference Guide for more details.
+//
+// NOTE: these values are valid *only* if you use the default mapping and are
+// provided as a convenience. If you use your own mapping, these values
+// are invalid and should not be used.
+
+enum
+{
+ TALKBACK_SPEECH_TARGET_INVALID = -1,
+ TALKBACK_SPEECH_TARGET_FIRST = 0,
+ TALKBACK_SPEECH_TARGET_EAT = TALKBACK_SPEECH_TARGET_FIRST, // 0
+ TALKBACK_SPEECH_TARGET_EARTH, // 1
+ TALKBACK_SPEECH_TARGET_IF, // 2
+ TALKBACK_SPEECH_TARGET_OX, // 3
+ TALKBACK_SPEECH_TARGET_OAT, // 4
+ TALKBACK_SPEECH_TARGET_WET, // 5
+ TALKBACK_SPEECH_TARGET_SIZE, // 6
+ TALKBACK_SPEECH_TARGET_CHURCH, // 7
+ TALKBACK_SPEECH_TARGET_FAVE, // 8
+ TALKBACK_SPEECH_TARGET_THOUGH, // 9
+ TALKBACK_SPEECH_TARGET_TOLD, // 10
+ TALKBACK_SPEECH_TARGET_BUMP, // 11
+ TALKBACK_SPEECH_TARGET_NEW, // 12
+ TALKBACK_SPEECH_TARGET_ROAR, // 13
+ TALKBACK_SPEECH_TARGET_CAGE, // 14
+ TALKBACK_SPEECH_TARGET_LAST = TALKBACK_SPEECH_TARGET_CAGE, // 14
+ TALKBACK_NUM_SPEECH_TARGETS // 15 (0..14)
+};
+
+// Speech gesture track identifiers.
+
+enum
+{
+ TALKBACK_GESTURE_INVALID = -1,
+ TALKBACK_GESTURE_FIRST = 0,
+ TALKBACK_GESTURE_EYEBROW_RAISE_LEFT = TALKBACK_GESTURE_FIRST, // 0
+ TALKBACK_GESTURE_EYEBROW_RAISE_RIGHT, // 1
+ TALKBACK_GESTURE_BLINK_LEFT, // 2
+ TALKBACK_GESTURE_BLINK_RIGHT, // 3
+ TALKBACK_GESTURE_HEAD_BEND, // 4
+ TALKBACK_GESTURE_HEAD_SIDE_SIDE, // 5
+ TALKBACK_GESTURE_HEAD_TWIST, // 6
+ TALKBACK_GESTURE_EYE_SIDE_SIDE_LEFT, // 7
+ TALKBACK_GESTURE_EYE_SIDE_SIDE_RIGHT, // 8
+ TALKBACK_GESTURE_EYE_UP_DOWN_LEFT, // 9
+ TALKBACK_GESTURE_EYE_UP_DOWN_RIGHT, // 10
+ TALKBACK_GESTURE_LAST = TALKBACK_GESTURE_EYE_UP_DOWN_RIGHT, // 10
+ TALKBACK_NUM_GESTURES // 11 (0..10)
+};
+
+// Phoneme identifiers.
+
+enum
+{
+ TALKBACK_PHONEME_INVALID = -1,
+ TALKBACK_PHONEME_FIRST = 0,
+ TALKBACK_PHONEME_IY = TALKBACK_PHONEME_FIRST, // 0
+ TALKBACK_PHONEME_IH, // 1
+ TALKBACK_PHONEME_EH, // 2
+ TALKBACK_PHONEME_EY, // 3
+ TALKBACK_PHONEME_AE, // 4
+ TALKBACK_PHONEME_AA, // 5
+ TALKBACK_PHONEME_AW, // 6
+ TALKBACK_PHONEME_AY, // 7
+ TALKBACK_PHONEME_AH, // 8
+ TALKBACK_PHONEME_AO, // 9
+ TALKBACK_PHONEME_OY, // 10
+ TALKBACK_PHONEME_OW, // 11
+ TALKBACK_PHONEME_UH, // 12
+ TALKBACK_PHONEME_UW, // 13
+ TALKBACK_PHONEME_ER, // 14
+ TALKBACK_PHONEME_AX, // 15
+ TALKBACK_PHONEME_S, // 16
+ TALKBACK_PHONEME_SH, // 17
+ TALKBACK_PHONEME_Z, // 18
+ TALKBACK_PHONEME_ZH, // 19
+ TALKBACK_PHONEME_F, // 20
+ TALKBACK_PHONEME_TH, // 21
+ TALKBACK_PHONEME_V, // 22
+ TALKBACK_PHONEME_DH, // 23
+ TALKBACK_PHONEME_M, // 24
+ TALKBACK_PHONEME_N, // 25
+ TALKBACK_PHONEME_NG, // 26
+ TALKBACK_PHONEME_L, // 27
+ TALKBACK_PHONEME_R, // 28
+ TALKBACK_PHONEME_W, // 29
+ TALKBACK_PHONEME_Y, // 30
+ TALKBACK_PHONEME_HH, // 31
+ TALKBACK_PHONEME_B, // 32
+ TALKBACK_PHONEME_D, // 33
+ TALKBACK_PHONEME_JH, // 34
+ TALKBACK_PHONEME_G, // 35
+ TALKBACK_PHONEME_P, // 36
+ TALKBACK_PHONEME_T, // 37
+ TALKBACK_PHONEME_K, // 38
+ TALKBACK_PHONEME_CH, // 39
+ TALKBACK_PHONEME_SIL, // 40
+ TALKBACK_PHONEME_LAST = TALKBACK_PHONEME_SIL, // 40
+ TALKBACK_NUM_PHONEMES // 41 (0..40)
+};
+
+// -----------------------------------------------------------------------------
+// Function declarations.
+
+// ---------------------------
+// Startup/shutdown functions.
+// ---------------------------
+
+// Must be the first function called when using TalkBack.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackStartupLibrary(
+ char const *iCoreDataDir); // IN: full path of folder containing TalkBack data files.
+
+// Should be the last function called when using TalkBack.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackShutdownLibrary(); // IN: nothing.
+
+// ------------------
+// Version functions.
+// ------------------
+
+// Gets the TalkBack version number.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetVersion(
+ long *oMajor, // OUT: major version number.
+ long *oMinor, // OUT: minor version number.
+ long *oRevision); // OUT: revision version number.
+
+// Gets the TalkBack version number as a string.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetVersionString(
+ long iMaxChars, // IN: size of version string buffer.
+ char *oVersion); // OUT: version string buffer.
+
+// ------------------
+// Utility functions.
+// ------------------
+
+// Checks whether a sound file can be analyzed and returns some quality metrics.
+//
+// NOTE: this function is deprecated and has been supplanted by
+// TalkBackGetSoundFileMetrics().
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackCheckSoundFile(
+ char const *iSoundFileName, // IN: name of sound file to be checked.
+ long *oCanBeAnalyzed, // OUT: 1 if sound can be analyzed, 0 if not.
+ long *oIsClipped, // OUT: 1 if sound is clipped, 0 if not.
+ double *oDecibelRange); // OUT: used decibel range of sound.
+
+// Returns metrics for the specified sound file.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetSoundFileMetrics(
+ char const *iSoundFileName, // IN: name of sound file to be checked.
+ TALKBACK_SOUND_FILE_METRICS *ioMetrics); // IN/OUT: address of a structure where the metrics will be stored.
+
+// Checks whether text can be used for text-based analysis, returning the text
+// as it will be analyzed.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackCheckSpokenText(
+ char const *iSpokenText, // IN: text to check.
+ long iMaxChars, // IN: size of analyzed text buffer.
+ char *oAnalyzedText); // OUT: buffer for text as it will be analyzed.
+
+// Convert a TalkBack error code to a description string.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetErrorString(
+ TALKBACK_ERR iErrorCode, // IN: TalkBack error code to convert.
+ long iMaxChars, // IN: size of the buffer.
+ char *oErrorString); // OUT: buffer for the description string.
+
+// Gets the error code and text for the most recent TalkBack error.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetLastError(
+ long iMaxChars, // IN: size of the buffer.
+ char *oErrorString, // OUT: buffer for the description string.
+ TALKBACK_ERR *oErrorCode); // OUT: most recent TalkBack error code.
+
+// -------------------
+// Analysis functions.
+// -------------------
+
+// Gets an opaque TALKBACK_ANALYSIS object. This object is then queried with the
+// TalkBackGet* functions below.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetAnalysis(
+ TALKBACK_ANALYSIS **ioAnalysis, // IN/OUT: address of a TALKBACK_ANALYSIS *variable where analysis will be stored.
+ char const *iSoundFileName, // IN: name of the sound file to analyze.
+ char const *iSoundText, // IN: text spoken in sound file (can be NULL to use textless analysis).
+ TALKBACK_ANALYSIS_SETTINGS *iSettings); // IN: pointer to a TALKBACK_ANALYSIS_SETTINGS structure (can be NULL for defaults).
+
+// Frees an opaque TALKBACK_ANALYSIS object. This releases all memory used by
+// the analysis.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackFreeAnalysis(
+ TALKBACK_ANALYSIS **ioAnalysis); // IN/OUT: analysis to free.
+
+// #######################################################################
+// NOTE: all functions from this point on require a valid analysis object.
+// #######################################################################
+
+// ------------------------
+// Speech target functions.
+// ------------------------
+
+// Gets the number of speech target tracks.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetNumSpeechTargetTracks(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long *oResult); // OUT: number of speech target tracks.
+
+// Gets the number of keys in the specified speech target track.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetNumSpeechTargetKeys(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech target track.
+ long *oResult); // OUT: number of keys in the speech target track.
+
+// Gets key information (time, value, derivative in, and derivative out) for the
+// specified key in the specified speech target track.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetSpeechTargetKeyInfo(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech target track.
+ long iKeyNum, // IN: speech target key.
+ double *oTime, // OUT: time of key.
+ double *oValue, // OUT: value of key.
+ double *oDerivativeIn, // OUT: incoming derivative of key.
+ double *oDerivativeOut); // OUT: outgoing derivative of key.
+
+// Gets the value of the function curve for the specified speech target track at
+// the specified time.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetSpeechTargetValueAtTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech target track.
+ double iTime, // IN: time in seconds.
+ double *oResult); // OUT: value of the function curve.
+
+// Gets the derivatives of the function curve for the specified speech target
+// track at the specified time.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetSpeechTargetDerivativesAtTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech target track.
+ double iTime, // IN: time in seconds.
+ double *oDerivativeIn, // OUT: value of the incoming derivative of the function curve.
+ double *oDerivativeOut); // OUT: value of the outgoing derivative of the function curve.
+
+// -------------------------
+// Speech gesture functions.
+// -------------------------
+
+// Gets the number of speech gesture tracks.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetNumGestureTracks(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long *oResult); // OUT: number of speech gesture tracks
+
+// Gets the number of keys in the specified speech gesture track.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetNumGestureKeys(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech gesture track.
+ long *oResult); // OUT: number of keys in the speech gesture track.
+
+// Gets key information (time, value, derivative in, and derivative out) for the
+// specified key in the specified speech gesture track.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetGestureKeyInfo(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech gesture track.
+ long iKeyNum, // IN: speech gesture key.
+ double *oTime, // OUT: time of key.
+ double *oValue, // OUT: value of key.
+ double *oDerivativeIn, // OUT: incoming derivative of key.
+ double *oDerivativeOut); // OUT: outgoing derivative of key.
+
+// Gets the value of the function curve for the specified speech gesture track
+// at the specified time.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetGestureValueAtTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech gesture track.
+ double iTime, // IN: time in seconds.
+ double *oResult); // OUT: value of the function curve.
+
+// Gets the derivatives of the function curve for the specified speech gesture
+// track at the specified time.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetGestureDerivativesAtTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech gesture track.
+ double iTime, // IN: time in seconds.
+ double *oDerivativeIn, // OUT: value of the incoming derivative of the function curve.
+ double *oDerivativeOut); // OUT: value of the outgoing derivative of the function curve.
+
+// ----------------
+// Frame functions.
+// ----------------
+
+// NOTE: these functions use the frame rate specified in the
+// TALKBACK_ANALYSIS_SETTINGS structure passed to TalkBackGetAnalysis() and
+// default to 30 fps (TALKBACK_DEFAULT_FRAME_RATE) if the structure pointer was
+// NULL.
+
+// Gets the first frame number.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetFirstFrameNum(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long *oResult); // OUT: number of the first frame.
+
+// Gets the last frame number.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetLastFrameNum(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long *oResult); // OUT: number of the last frame.
+
+// Gets the start time of the specified frame.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetFrameStartTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iFrameNum, // IN: frame.
+ double *oResult); // OUT: start time of the frame in seconds.
+
+// Gets the end time of the specified frame.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetFrameEndTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iFrameNum, // IN: frame.
+ double *oResult); // OUT: end time of the frame in seconds.
+
+// Gets the value of the function curve for a speech target integrated over the
+// specified frame.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetSpeechTargetValueAtFrame(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech target track.
+ long iFrameNum, // IN: frame number.
+ double *oResult); // OUT: value of the function curve integrated over the frame.
+
+// Gets the dominant speech target at the specified frame.
+//
+// NOTE: this function is meant to be used in flipbook mode only.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetDominantSpeechTargetAtFrame(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iFrameNum, // IN: frame number.
+ TALKBACK_SPEECH_TARGET *oSpeechTarget); // OUT: dominant speech target.
+
+// Gets the value of the function curve for a speech gesture integrated over the
+// specified frame.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetGestureValueAtFrame(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iTrackNum, // IN: speech gesture track.
+ long iFrameNum, // IN: frame number.
+ double *oResult); // OUT: value of the function curve integrated over the frame.
+
+// ------------------
+// Phoneme functions.
+// ------------------
+
+// Gets the number of phonemes.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetNumPhonemes(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long *oResult); // OUT: number of phonemes.
+
+// Gets the enumeration of the specified phoneme.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetPhonemeEnum(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iPhonemeNum, // IN: phoneme.
+ TALKBACK_PHONEME *oResult); // OUT: enumeration of the specified phoneme.
+
+// Gets the start time of the specified phoneme.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetPhonemeStartTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iPhonemeNum, // IN: phoneme.
+ double *oResult); // OUT: start time of the phoneme in seconds.
+
+// Gets the end time of the specified phoneme.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetPhonemeEndTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iPhonemeNum, // IN: phoneme.
+ double *oResult); // OUT: end time of the phoneme in seconds.
+
+// ---------------
+// Word functions.
+// ---------------
+
+// NOTE: these functions only yield data for text-based analysis.
+
+// Gets the number of words.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetNumWords(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long *oResult); // OUT: number of words.
+
+// Gets the text of the specified word.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetWord(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iWordNum, // IN: word.
+ long iMaxChars, // IN: size of word buffer.
+ char *oWord); // OUT: word buffer.
+
+// Gets the start time of the specified word.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetWordStartTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iWordNum, // IN: word.
+ double *oResult); // OUT: start time of the word in seconds.
+
+// Gets the end time of the specified word.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackGetWordEndTime(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iWordNum, // IN: word.
+ double *oResult); // OUT: end time of the word in seconds.
+
+// --------------------------
+// Phoneme editing functions.
+// --------------------------
+
+// Use these functions to modify the phoneme list after you get an opaque
+// analysis object from TalkBackGetAnalysis(). After modifying the phoneme list
+// in the opaque analysis object, subsequent TalkBackGet* calls on that opaque
+// analysis object for speech target (lip-synching) data will return values
+// based on the modified phoneme list. However, speech gesture data is not
+// affected by phoneme editing.
+//
+// NOTE: phoneme editing is only provided in order to support Ventriloquist-like
+// applications where tweaking of the phoneme segmenation (and subsequent
+// recalculation of the animation data) is required. Most customers probably
+// won't need this functionality.
+
+// Inserts a phoneme at the specified position in the specified manner.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackInsertPhoneme(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ TALKBACK_PHONEME iPhoneme, // IN: enumeration of phoneme to insert.
+ long iInsertPosition, // IN: position (phoneme number) at which to insert.
+ int iInsertBefore); // IN: manner of insertion:
+ // 0 means put phoneme after insert position;
+ // 1 means put phoneme before insert position.
+
+// Deletes the specified phoneme.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackDeletePhoneme(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iPhonemeToDelete); // IN: phoneme to delete.
+
+// Changes the start time of the specified phoneme.
+//
+// NOTE: the start time specified may not be the actual start time for a number
+// of reasons, most notably if the specified start time will make the phoneme
+// too short. This function returns the actual start time so the caller can
+// check the result without having to query the phoneme.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackChangePhonemeStart(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iPhonemeToChange, // IN: phoneme to change.
+ double *ioNewTime); // IN/OUT: new start time value in seconds (in); actual start time (out).
+
+// Changes the end time of the specified phoneme.
+//
+// NOTE: the end time specified may not be the actual end time for a number of
+// reasons, most notably if the specified end time will make the phoneme too
+// short. This function returns the actual end time so the caller can check the
+// result without having to query the phoneme.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackChangePhonemeEnd(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iPhonemeToChange, // IN: phoneme to change.
+ double *ioNewTime); // IN/OUT: new end time value in seconds (in); actual end time (out).
+
+// Changes the enumeration of the specified phoneme.
+TALKBACK_ERR // RETURNS: TALKBACK_NOERR if successful, TalkBack error code if not.
+TalkBackChangePhonemeEnum(
+ TALKBACK_ANALYSIS *iAnalysis, // IN: opaque analysis object returned by TalkBackGetAnalysis().
+ long iPhonemeToChange, // IN: phoneme to change.
+ TALKBACK_PHONEME iNewPhoneme); // IN: new phoneme enumeration.
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/mp/src/utils/qc_eyes/QC_Eyes.cpp b/mp/src/utils/qc_eyes/QC_Eyes.cpp new file mode 100644 index 00000000..d62fe124 --- /dev/null +++ b/mp/src/utils/qc_eyes/QC_Eyes.cpp @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+// QC_Eyes.cpp : Defines the class behaviors for the application.
+//
+
+#include "stdafx.h"
+#include "QC_Eyes.h"
+#include "QC_EyesDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CQC_EyesApp
+
+BEGIN_MESSAGE_MAP(CQC_EyesApp, CWinApp)
+ //{{AFX_MSG_MAP(CQC_EyesApp)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG
+ ON_COMMAND(ID_HELP, CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CQC_EyesApp construction
+
+CQC_EyesApp::CQC_EyesApp()
+{
+ // TODO: add construction code here,
+ // Place all significant initialization in InitInstance
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// The one and only CQC_EyesApp object
+
+CQC_EyesApp theApp;
+
+/////////////////////////////////////////////////////////////////////////////
+// CQC_EyesApp initialization
+
+BOOL CQC_EyesApp::InitInstance()
+{
+ AfxEnableControlContainer();
+
+ // Standard initialization
+ // If you are not using these features and wish to reduce the size
+ // of your final executable, you should remove from the following
+ // the specific initialization routines you do not need.
+
+#ifdef _AFXDLL
+ Enable3dControls(); // Call this when using MFC in a shared DLL
+#endif
+
+ CQC_EyesDlg dlg;
+ m_pMainWnd = &dlg;
+ INT_PTR nResponse = dlg.DoModal();
+ if (nResponse == IDOK)
+ {
+ // TODO: Place code here to handle when the dialog is
+ // dismissed with OK
+ }
+ else if (nResponse == IDCANCEL)
+ {
+ // TODO: Place code here to handle when the dialog is
+ // dismissed with Cancel
+ }
+
+ // Since the dialog has been closed, return FALSE so that we exit the
+ // application, rather than start the application's message pump.
+ return FALSE;
+}
diff --git a/mp/src/utils/qc_eyes/QC_Eyes.h b/mp/src/utils/qc_eyes/QC_Eyes.h new file mode 100644 index 00000000..240cb3d1 --- /dev/null +++ b/mp/src/utils/qc_eyes/QC_Eyes.h @@ -0,0 +1,50 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+// QC_Eyes.h : main header file for the QC_EYES application
+//
+
+#if !defined(AFX_QC_EYES_H__398BAF8D_D3C0_4326_BEF0_5129884EE1A3__INCLUDED_)
+#define AFX_QC_EYES_H__398BAF8D_D3C0_4326_BEF0_5129884EE1A3__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h" // main symbols
+
+/////////////////////////////////////////////////////////////////////////////
+// CQC_EyesApp:
+// See QC_Eyes.cpp for the implementation of this class
+//
+
+class CQC_EyesApp : public CWinApp
+{
+public:
+ CQC_EyesApp();
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CQC_EyesApp)
+ public:
+ virtual BOOL InitInstance();
+ //}}AFX_VIRTUAL
+
+// Implementation
+
+ //{{AFX_MSG(CQC_EyesApp)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_QC_EYES_H__398BAF8D_D3C0_4326_BEF0_5129884EE1A3__INCLUDED_)
diff --git a/mp/src/utils/qc_eyes/QC_Eyes.rc b/mp/src/utils/qc_eyes/QC_Eyes.rc new file mode 100644 index 00000000..c0cafafc --- /dev/null +++ b/mp/src/utils/qc_eyes/QC_Eyes.rc @@ -0,0 +1,276 @@ +// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+ "#define _AFX_NO_OLE_RESOURCES\r\n"
+ "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+ "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+ "\r\n"
+ "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif //_WIN32\r\n"
+ "#include ""res\\QC_Eyes.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#endif\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME ICON "res\\QC_Eyes.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_QC_EYES_DIALOG DIALOGEX 0, 0, 695, 510
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+EXSTYLE WS_EX_APPWINDOW
+CAPTION "QC Eyes"
+FONT 8, "MS Sans Serif", 0, 0, 0x1
+BEGIN
+ EDITTEXT IDC_REFERENCE_FILENAME,87,24,93,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_EXPRESSIONS_FILENAME,87,42,93,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_MODEL_FILENAME,87,60,93,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_RIGHT_EYE_X,19,140,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_RIGHT_EYE_Y,83,140,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_RIGHT_EYE_Z,147,140,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LEFT_EYE_X,19,205,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LEFT_EYE_Y,83,205,40,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LEFT_EYE_Z,147,205,40,14,ES_AUTOHSCROLL
+ GROUPBOX "3D Platform",IDC_STATIC,219,7,126,47
+ GROUPBOX "Options",IDC_STATIC,219,58,126,47
+ GROUPBOX "Upper Lid Positions",IDC_STATIC,219,109,126,60
+ EDITTEXT IDC_UPPER_LID_RAISED,264,118,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_UPPER_LID_NEUTRAL,264,134,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_UPPER_LID_LOWERED,264,150,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOWER_LID_RAISED,264,184,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOWER_LID_NEUTRAL,264,200,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOWER_LID_LOWERED,264,216,74,14,ES_AUTOHSCROLL
+ GROUPBOX "Iris Color",IDC_STATIC,351,175,75,60
+ GROUPBOX "Eye White Color",IDC_STATIC,431,175,126,60
+ EDITTEXT IDC_IRIS_SIZE,469,21,59,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_EYEBALL_SIZE,469,43,59,14,ES_AUTOHSCROLL
+ PUSHBUTTON "Create QC Text",IDC_CREATE_QC_TEXT,7,243,93,19
+ PUSHBUTTON "Copy Text To Clipboard",IDC_COPY_TEXT_TO_CLIPBOARD,109,
+ 243,93,19
+ EDITTEXT IDC_OUTPUT_TEXT,7,271,681,232,ES_MULTILINE |
+ ES_AUTOHSCROLL
+ CONTROL "Y Axis Up (XSI, Maya)",IDC_Y_AXIS_UP,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,228,21,92,10
+ CONTROL "Z Axis Up (MAX)",IDC_Z_AXIS_UP,"Button",
+ BS_AUTORADIOBUTTON,228,36,94,10
+ CONTROL "Default Controls",IDC_DEFAULT_CONTROLS,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,228,72,92,10
+ CONTROL "Advanced Controls",IDC_ADVANCED_CONTROLS,"Button",
+ BS_AUTORADIOBUTTON,228,88,94,10
+ RADIOBUTTON "Brown Irises",IDC_IRIS_COLOR_BROWN,360,188,55,10,
+ WS_GROUP
+ RADIOBUTTON "Green Irises",IDC_IRIS_COLOR_GREEN,360,203,54,10
+ RADIOBUTTON "Blue Irises",IDC_IRIS_COLOR_BLUE,360,218,54,10
+ RADIOBUTTON "Light Whites (paler skin tones)",IDC_EYE_COLOR_LIGHT,
+ 438,193,108,10,WS_GROUP
+ RADIOBUTTON "Dark Whites (darker skin tones)",IDC_EYE_COLOR_DARK,438,
+ 214,112,10
+ RTEXT "Reference Filename:",IDC_STATIC,13,27,70,8
+ RTEXT "Expressions Filename:",IDC_STATIC,13,45,70,8
+ RTEXT "Model Filename:",IDC_STATIC,13,64,70,8
+ LTEXT ".SMD",IDC_STATIC,184,29,19,8
+ LTEXT ".VTA",IDC_STATIC,184,47,17,8
+ LTEXT ".MDL",IDC_STATIC,184,65,18,8
+ GROUPBOX "Eye Positions",IDC_STATIC,7,95,200,140
+ GROUPBOX "Right Eye",IDC_STATIC,14,108,185,56
+ LTEXT "Y Position",IDC_STATIC,85,126,32,8
+ LTEXT "X Position",IDC_STATIC,21,126,32,8
+ LTEXT "Z Position",IDC_STATIC,149,126,32,8
+ GROUPBOX "Left Eye",IDC_STATIC,14,173,185,56
+ LTEXT "Y Position",IDC_STATIC,85,191,32,8
+ LTEXT "X Position",IDC_STATIC,21,191,32,8
+ LTEXT "Z Position",IDC_STATIC,149,191,32,8
+ GROUPBOX "Filenames",IDC_STATIC,7,7,200,78
+ CONTROL "",IDC_PICTURES,"Static",SS_BITMAP,352,7,336,162
+ RTEXT "Raised:",IDC_STATIC,228,122,32,8
+ RTEXT "Neutral:",IDC_STATIC,227,137,32,8
+ RTEXT "Lowered:",IDC_STATIC,227,153,32,8
+ GROUPBOX "Lowered Lid Positions",IDC_STATIC,219,175,126,60
+ RTEXT "Raised:",IDC_STATIC,228,188,32,8
+ RTEXT "Neutral:",IDC_STATIC,227,204,32,8
+ RTEXT "Lowered:",IDC_STATIC,227,220,32,8
+ GROUPBOX "Eye Detail Control",IDC_EYE_DETAIL_CONTROL_FRAME,414,7,
+ 140,73
+ RTEXT "Iris Size:",IDC_IRIS_SIZE_LABEL,417,25,48,8
+ RTEXT "Eyeball Size:",IDC_EYEBALL_SIZE_LABEL,417,47,48,8
+ CTEXT "",IDC_PICTURE_LABEL,375,162,285,8
+ CONTROL "Enable Independent Left Lid Control",
+ IDC_LEFT_LID_CONTROL,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,421,62,128,10
+ GROUPBOX "Upper Left Lid Positions",IDC_UPPER_LEFT_LID_PANEL,562,
+ 7,126,60
+ EDITTEXT IDC_UPPER_LEFT_LID_RAISED,607,16,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_UPPER_LEFT_LID_NEUTRAL,607,32,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_UPPER_LEFT_LID_LOWERED,607,48,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOWER_LEFT_LID_RAISED,607,82,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOWER_LEFT_LID_NEUTRAL,607,98,74,14,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOWER_LEFT_LID_LOWERED,607,114,74,14,ES_AUTOHSCROLL
+ RTEXT "Raised:",IDC_UPPER_LEFT_LID_RAISED_LABEL,571,20,32,8
+ RTEXT "Neutral:",IDC_UPPER_LEFT_LID_NEUTRAL_LABEL,570,35,32,8
+ RTEXT "Lowered:",IDC_UPPER_LEFT_LID_LOWERED_LABEL,570,51,32,8
+ GROUPBOX "Lowered Left Lid Positions",IDC_LOWER_LEFT_LID_PANEL,
+ 562,73,126,60
+ RTEXT "Raised:",IDC_LOWER_LEFT_LID_RAISED_LABEL,571,86,32,8
+ RTEXT "Neutral:",IDC_LOWER_LEFT_LID_NEUTRAL_LABEL,570,102,32,8
+ RTEXT "Lowered:",IDC_LOWER_LEFT_LID_LOWERED_LABEL,570,118,32,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "FileDescription", "QC_Eyes MFC Application"
+ VALUE "FileVersion", "1, 0, 0, 1"
+ VALUE "InternalName", "QC_Eyes"
+ VALUE "LegalCopyright", "Copyright (C) 2005"
+ VALUE "OriginalFilename", "QC_Eyes.EXE"
+ VALUE "ProductName", "QC_Eyes Application"
+ VALUE "ProductVersion", "1, 0, 0, 1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_QC_EYES_DIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 688
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 503
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_EYE_DEFAULT BITMAP "res\\eye_default.bmp"
+IDB_EYE_LOWER_HI BITMAP "res\\eye_lower_hi.bmp"
+IDB_EYE_LOWER_LO BITMAP "res\\eye_lower_lo.bmp"
+IDB_EYE_LOWER_MID BITMAP "res\\eye_lower_mid.bmp"
+IDB_EYE_UPPER_HI BITMAP "res\\eye_upper_hi.bmp"
+IDB_EYE_UPPER_LO BITMAP "res\\eye_upper_lo.bmp"
+IDB_EYE_UPPER_MID BITMAP "res\\eye_upper_mid.bmp"
+IDB_EYE_XY_L BITMAP "res\\eye_xy_l.bmp"
+IDB_EYE_XY_R BITMAP "res\\eye_xy_r.bmp"
+IDB_EYE_Z_L BITMAP "res\\eye_z_l.bmp"
+IDB_EYE_Z_R BITMAP "res\\eye_z_r.bmp"
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "res\QC_Eyes.rc2" // non-Microsoft Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/mp/src/utils/qc_eyes/QC_EyesDlg.cpp b/mp/src/utils/qc_eyes/QC_EyesDlg.cpp new file mode 100644 index 00000000..863e556a --- /dev/null +++ b/mp/src/utils/qc_eyes/QC_EyesDlg.cpp @@ -0,0 +1,705 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+// QC_EyesDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include <math.h>
+#include "QC_Eyes.h"
+#include "QC_EyesDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CQC_EyesDlg dialog
+
+CQC_EyesDlg::CQC_EyesDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CQC_EyesDlg::IDD, pParent)
+{
+ //{{AFX_DATA_INIT(CQC_EyesDlg)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+ // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
+ m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
+ m_pBitmapHead = NULL;
+}
+
+void CQC_EyesDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CQC_EyesDlg)
+ DDX_Control(pDX, IDC_LEFT_LID_CONTROL, m_IndependentLeftLidControl);
+ DDX_Control(pDX, IDC_PICTURES, m_PictureControl);
+ //}}AFX_DATA_MAP
+}
+
+BEGIN_MESSAGE_MAP(CQC_EyesDlg, CDialog)
+ //{{AFX_MSG_MAP(CQC_EyesDlg)
+ ON_WM_PAINT()
+ ON_WM_QUERYDRAGICON()
+ ON_BN_CLICKED(IDC_CREATE_QC_TEXT, OnCreateQcText)
+ ON_BN_CLICKED(IDC_IRIS_COLOR_BROWN, OnIrisColorBrown)
+ ON_BN_CLICKED(IDC_IRIS_COLOR_GREEN, OnIrisColorGreen)
+ ON_BN_CLICKED(IDC_IRIS_COLOR_BLUE, OnIrisColorBlue)
+ ON_BN_CLICKED(IDC_EYE_COLOR_DARK, OnEyeColorDark)
+ ON_BN_CLICKED(IDC_EYE_COLOR_LIGHT, OnEyeColorLight)
+ ON_EN_SETFOCUS(IDC_RIGHT_EYE_X, OnSetfocusRightEyeX)
+ ON_EN_SETFOCUS(IDC_RIGHT_EYE_Y, OnSetfocusRightEyeY)
+ ON_EN_SETFOCUS(IDC_RIGHT_EYE_Z, OnSetfocusRightEyeZ)
+ ON_EN_SETFOCUS(IDC_LEFT_EYE_X, OnSetfocusLeftEyeX)
+ ON_EN_SETFOCUS(IDC_LEFT_EYE_Y, OnSetfocusLeftEyeY)
+ ON_EN_SETFOCUS(IDC_LEFT_EYE_Z, OnSetfocusLeftEyeZ)
+ ON_EN_SETFOCUS(IDC_UPPER_LID_LOWERED, OnSetfocusUpperLidLowered)
+ ON_EN_SETFOCUS(IDC_UPPER_LID_NEUTRAL, OnSetfocusUpperLidNeutral)
+ ON_EN_SETFOCUS(IDC_UPPER_LID_RAISED, OnSetfocusUpperLidRaised)
+ ON_EN_SETFOCUS(IDC_LOWER_LID_LOWERED, OnSetfocusLowerLidLowered)
+ ON_EN_SETFOCUS(IDC_LOWER_LID_NEUTRAL, OnSetfocusLowerLidNeutral)
+ ON_EN_SETFOCUS(IDC_LOWER_LID_RAISED, OnSetfocusLowerLidRaised)
+ ON_BN_CLICKED(IDC_COPY_TEXT_TO_CLIPBOARD, OnCopyTextToClipboard)
+ ON_BN_CLICKED(IDC_DEFAULT_CONTROLS, OnDefaultControls)
+ ON_BN_CLICKED(IDC_ADVANCED_CONTROLS, OnAdvancedControls)
+ ON_BN_CLICKED(IDC_LEFT_LID_CONTROL, OnLeftLidControl)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+void CQC_EyesDlg::AddText( const char *pFormat, ... )
+{
+ char tempMsg[4096];
+ va_list marker;
+ va_start( marker, pFormat );
+ _vsnprintf( tempMsg, sizeof( tempMsg ), pFormat, marker );
+ tempMsg[ ARRAYSIZE(tempMsg) - 1 ] = 0;
+ va_end( marker );
+
+ size_t nCharsInBuf = strlen( m_Buf );
+ size_t nCharsInMsg = strlen( tempMsg );
+
+ if ( nCharsInBuf + nCharsInMsg + 1 > m_BufSize )
+ {
+ m_BufSize = nCharsInBuf + nCharsInMsg + 4096;
+ char *newbuf = new char[m_BufSize];
+ memcpy( newbuf, m_Buf, nCharsInBuf + 1 );
+ delete [] m_Buf;
+ m_Buf = newbuf;
+ }
+
+ strcat( m_Buf, tempMsg );
+}
+
+
+void SendToEditControl( HWND hEditControl, const char *pText )
+{
+ LRESULT nLen = SendMessage( hEditControl, EM_GETLIMITTEXT, 0, 0 );
+ SendMessage( hEditControl, EM_SETSEL, nLen, nLen );
+ SendMessage( hEditControl, EM_REPLACESEL, FALSE, (LPARAM)pText );
+}
+
+
+void FormatAndSendToEditControl( void *hWnd, const char *pText )
+{
+ HWND hEditControl = (HWND)hWnd;
+
+ // Translate \n to \r\n.
+ char outMsg[1024];
+ const char *pIn = pText;
+ char *pOut = outMsg;
+ while ( *pIn )
+ {
+ if ( *pIn == '\n' )
+ {
+ *pOut = '\r';
+ pOut++;
+ }
+ *pOut = *pIn;
+
+ ++pIn;
+ ++pOut;
+
+ if ( pOut - outMsg >= 1020 )
+ {
+ *pOut = 0;
+ SendToEditControl( hEditControl, outMsg );
+ pOut = outMsg;
+ }
+ }
+ *pOut = 0;
+ SendToEditControl( hEditControl, outMsg );
+}
+
+
+HBITMAP CQC_EyesDlg::GetCachedBitmap( UINT id )
+{
+ for ( CBitmapRef *pCur=m_pBitmapHead; pCur; pCur=pCur->m_pNext )
+ {
+ if ( pCur->m_iResource == id )
+ return pCur->m_hBitmap;
+ }
+
+ CBitmapRef *pNew = new CBitmapRef;
+ pNew->m_iResource = id;
+ pNew->m_hBitmap = ::LoadBitmap( AfxGetInstanceHandle(), MAKEINTRESOURCE(id) );
+ pNew->m_pNext = m_pBitmapHead;
+ m_pBitmapHead = pNew;
+
+ return pNew->m_hBitmap;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CQC_EyesDlg message handlers
+
+BOOL CQC_EyesDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+ // Set the icon for this dialog. The framework does this automatically
+ // when the application's main window is not a dialog
+ SetIcon(m_hIcon, TRUE); // Set big icon
+ SetIcon(m_hIcon, FALSE); // Set small icon
+
+ // TODO: Add extra initialization here
+ GetDlgItem( IDC_REFERENCE_FILENAME )->SetWindowText( "filename_reference" );
+ GetDlgItem( IDC_EXPRESSIONS_FILENAME )->SetWindowText( "filename_expressions" );
+ GetDlgItem( IDC_MODEL_FILENAME )->SetWindowText( "filename_model" );
+ GetDlgItem( IDC_IRIS_SIZE )->SetWindowText( "0.63" );
+ GetDlgItem( IDC_EYEBALL_SIZE )->SetWindowText( "1.0" );
+
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_Y_AXIS_UP ), BM_SETCHECK, BST_CHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_DEFAULT_CONTROLS ), BM_SETCHECK, BST_CHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_BROWN ), BM_SETCHECK, BST_CHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_EYE_COLOR_LIGHT ), BM_SETCHECK, BST_CHECKED, 0 );
+
+ m_hOutputText = ::GetDlgItem( m_hWnd, IDC_OUTPUT_TEXT );
+
+ m_PictureControl.SetBitmap( GetCachedBitmap( IDB_EYE_DEFAULT ) );
+ OnDefaultControls(); // Hide the advanced controls.
+
+ return TRUE; // return TRUE unless you set the focus to a control
+}
+
+// If you add a minimize button to your dialog, you will need the code below
+// to draw the icon. For MFC applications using the document/view model,
+// this is automatically done for you by the framework.
+
+void CQC_EyesDlg::OnPaint()
+{
+ if (IsIconic())
+ {
+ CPaintDC dc(this); // device context for painting
+
+ SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
+
+ // Center icon in client rectangle
+ int cxIcon = GetSystemMetrics(SM_CXICON);
+ int cyIcon = GetSystemMetrics(SM_CYICON);
+ CRect rect;
+ GetClientRect(&rect);
+ int x = (rect.Width() - cxIcon + 1) / 2;
+ int y = (rect.Height() - cyIcon + 1) / 2;
+
+ // Draw the icon
+ dc.DrawIcon(x, y, m_hIcon);
+ }
+ else
+ {
+ CDialog::OnPaint();
+ }
+}
+
+// The system calls this to obtain the cursor to display while the user drags
+// the minimized window.
+HCURSOR CQC_EyesDlg::OnQueryDragIcon()
+{
+ return (HCURSOR) m_hIcon;
+}
+
+
+void HandleYAxisUp( float &yVal, float &zVal )
+{
+ float flTemp = yVal;
+ yVal = -zVal;
+ zVal = flTemp;
+}
+
+
+float CQC_EyesDlg::GetDlgItemFloat( UINT id )
+{
+ char text[4096];
+ GetDlgItemText( id, text, sizeof( text ) );
+ return (float)atof( text );
+}
+
+
+bool CQC_EyesDlg::IsOptionChecked( UINT option )
+{
+ return (::SendMessage( ::GetDlgItem( m_hWnd, option ), BM_GETCHECK, 0, 0 ) == BST_CHECKED);
+}
+
+
+void CQC_EyesDlg::GetDialogParams( CDialogParams &p )
+{
+ p.m_flLeftEye[0] = GetDlgItemFloat( IDC_LEFT_EYE_X );
+ p.m_flLeftEye[1] = GetDlgItemFloat( IDC_LEFT_EYE_Y );
+ p.m_flLeftEye[2] = GetDlgItemFloat( IDC_LEFT_EYE_Z );
+
+ p.m_flRightEye[0] = GetDlgItemFloat( IDC_RIGHT_EYE_X );
+ p.m_flRightEye[1] = GetDlgItemFloat( IDC_RIGHT_EYE_Y );
+ p.m_flRightEye[2] = GetDlgItemFloat( IDC_RIGHT_EYE_Z );
+
+ bool bYAxisUp = IsOptionChecked( IDC_Y_AXIS_UP );
+ if ( bYAxisUp )
+ {
+ HandleYAxisUp( p.m_flLeftEye[1], p.m_flLeftEye[2] );
+ HandleYAxisUp( p.m_flRightEye[1], p.m_flRightEye[2] );
+ }
+
+ GetDlgItemText( IDC_REFERENCE_FILENAME, p.m_ReferenceFilename, sizeof( p.m_ReferenceFilename ) );
+ GetDlgItemText( IDC_EXPRESSIONS_FILENAME, p.m_ExpressionsFilename, sizeof( p.m_ExpressionsFilename ) );
+ GetDlgItemText( IDC_MODEL_FILENAME, p.m_ModelFilename, sizeof( p.m_ModelFilename ) );
+
+ p.m_flIrisSize = GetDlgItemFloat( IDC_IRIS_SIZE );
+ p.m_flEyeballSize = GetDlgItemFloat( IDC_EYEBALL_SIZE );
+
+ p.m_flRightUpperLidRaised = GetDlgItemFloat( IDC_UPPER_LID_RAISED );
+ p.m_flRightUpperLidNeutral = GetDlgItemFloat( IDC_UPPER_LID_NEUTRAL );
+ p.m_flRightUpperLidLowered = GetDlgItemFloat( IDC_UPPER_LID_LOWERED );
+
+ p.m_flRightLowerLidRaised = GetDlgItemFloat( IDC_LOWER_LID_RAISED );
+ p.m_flRightLowerLidNeutral = GetDlgItemFloat( IDC_LOWER_LID_NEUTRAL );
+ p.m_flRightLowerLidLowered = GetDlgItemFloat( IDC_LOWER_LID_LOWERED );
+
+ if ( IsIndependentLeftLidControlEnabled() )
+ {
+ p.m_flLeftUpperLidRaised = GetDlgItemFloat( IDC_UPPER_LEFT_LID_RAISED );
+ p.m_flLeftUpperLidNeutral = GetDlgItemFloat( IDC_UPPER_LEFT_LID_NEUTRAL );
+ p.m_flLeftUpperLidLowered = GetDlgItemFloat( IDC_UPPER_LEFT_LID_LOWERED );
+
+ p.m_flLeftLowerLidRaised = GetDlgItemFloat( IDC_LOWER_LEFT_LID_RAISED );
+ p.m_flLeftLowerLidNeutral = GetDlgItemFloat( IDC_LOWER_LEFT_LID_NEUTRAL );
+ p.m_flLeftLowerLidLowered = GetDlgItemFloat( IDC_LOWER_LEFT_LID_LOWERED );
+ }
+ else
+ {
+ // Left lids follow the right lids.
+ p.m_flLeftUpperLidRaised = p.m_flRightUpperLidRaised;
+ p.m_flLeftUpperLidNeutral = p.m_flRightUpperLidNeutral;
+ p.m_flLeftUpperLidLowered = p.m_flRightUpperLidLowered;
+
+ p.m_flLeftLowerLidRaised = p.m_flRightLowerLidRaised;
+ p.m_flLeftLowerLidNeutral = p.m_flRightLowerLidNeutral;
+ p.m_flLeftLowerLidLowered = p.m_flRightLowerLidLowered;
+ }
+
+ // Figure out the eyeball prefix.
+ if ( IsOptionChecked( IDC_EYE_COLOR_LIGHT ) )
+ strcpy( p.m_EyeballPrefix, "eyeball" );
+ else
+ strcpy( p.m_EyeballPrefix, "dark_eyeball" );
+
+ // Figure out the pupil prefix.
+ if ( IsOptionChecked( IDC_IRIS_COLOR_BROWN ) )
+ strcpy( p.m_PupilPrefix, "pupil" );
+ else if ( IsOptionChecked( IDC_IRIS_COLOR_GREEN ) )
+ strcpy( p.m_PupilPrefix, "grn_pupil" );
+ else
+ strcpy( p.m_PupilPrefix, "bl_pupil" );
+}
+
+
+void CQC_EyesDlg::GenerateQCText()
+{
+ CDialogParams p;
+ GetDialogParams( p );
+
+
+ m_BufSize = 16 * 1024;
+ m_Buf = new char[m_BufSize];
+ m_Buf[0] = 0;
+
+ AddText( "//start eye/face data\n" );
+ AddText( "$eyeposition 0 0 70\n\n" );
+
+ AddText( "//head controllers\n" );
+ AddText( "$attachment \"eyes\" \"ValveBiped.Bip01_Head1\" %.2f %.2f %.2f absolute\n",
+ p.m_flLeftEye[0] - ((fabs( p.m_flRightEye[0] ) + p.m_flLeftEye[0]) * 0.5),
+ (p.m_flLeftEye[1] + p.m_flRightEye[1]) * 0.5,
+ (p.m_flLeftEye[2] + p.m_flRightEye[2]) * 0.5 );
+
+ AddText( "$attachment \"mouth\" \"ValveBiped.Bip01_Head1\" 0.80 -5.80 -0.15 rotate 0 -80 -90\n\n" );
+
+ AddText( "$model %s \"%s.smd\" {\n",
+ p.m_ModelFilename, p.m_ReferenceFilename );
+
+ AddText( "\teyeball righteye \"ValveBiped.Bip01_Head1\" %.2f %.2f %.2f \"%s_r\" %.2f 4 \"%s_r\" %.2f\n",
+ p.m_flRightEye[0],
+ p.m_flRightEye[1],
+ p.m_flRightEye[2],
+ p.m_EyeballPrefix,
+ p.m_flEyeballSize,
+ p.m_PupilPrefix,
+ p.m_flIrisSize );
+
+ AddText( "\teyeball lefteye \"ValveBiped.Bip01_Head1\" %.2f %.2f %.2f \"%s_l\" %.2f -4 \"%s_l\" %.2f\n\n",
+ p.m_flLeftEye[0],
+ p.m_flLeftEye[1],
+ p.m_flLeftEye[2],
+ p.m_EyeballPrefix,
+ p.m_flEyeballSize,
+ p.m_PupilPrefix,
+ p.m_flIrisSize );
+
+ AddText( "\teyelid upper_right \"%s\" lowerer 1 %.2f neutral 0 %.2f raiser 2 %.2f split 0.1 eyeball righteye\n",
+ p.m_ExpressionsFilename,
+ p.m_flRightUpperLidLowered - p.m_flRightEye[2],
+ p.m_flRightUpperLidNeutral - p.m_flRightEye[2],
+ p.m_flRightUpperLidRaised - p.m_flRightEye[2] );
+
+ AddText( "\teyelid lower_right \"%s\" lowerer 3 %.2f neutral 0 %.2f raiser 4 %.2f split 0.1 eyeball righteye\n",
+ p.m_ExpressionsFilename,
+ p.m_flRightLowerLidLowered - p.m_flRightEye[2],
+ p.m_flRightLowerLidNeutral - p.m_flRightEye[2],
+ p.m_flRightLowerLidRaised - p.m_flRightEye[2] );
+
+ AddText( "\teyelid upper_left \"%s\" lowerer 1 %.2f neutral 0 %.2f raiser 2 %.2f split -0.1 eyeball lefteye\n",
+ p.m_ExpressionsFilename,
+ p.m_flLeftUpperLidLowered - p.m_flLeftEye[2],
+ p.m_flLeftUpperLidNeutral - p.m_flLeftEye[2],
+ p.m_flLeftUpperLidRaised - p.m_flLeftEye[2] );
+
+ AddText( "\teyelid lower_left \"%s\" lowerer 3 %.2f neutral 0 %.2f raiser 4 %.2f split -0.1 eyeball lefteye\n\n",
+ p.m_ExpressionsFilename,
+ p.m_flLeftLowerLidLowered - p.m_flLeftEye[2],
+ p.m_flLeftLowerLidNeutral - p.m_flLeftEye[2],
+ p.m_flLeftLowerLidRaised - p.m_flLeftEye[2] );
+
+ AddText( "\tmouth 0 \"mouth\" \"ValveBiped.Bip01_Head1\" 0 1 0 // mouth illumination\n" );
+ AddText( "\tflexfile \"%s\" {\n", p.m_ExpressionsFilename );
+ AddText( "\t\t$include \"../standardflex_xsi.qci\"\n" );
+ AddText( "\t}\n" );
+ AddText( "\t$include \"../facerules_xsi.qci\"\n" );
+ AddText( "\t$include \"../bodyrules_xsi.qci\"\n" );
+ AddText( "}\n" );
+ AddText( "//end eye/face data\n" );
+}
+
+
+bool CQC_EyesDlg::CheckNumericInputs()
+{
+ struct
+ {
+ const char *pControlName;
+ UINT controlID;
+ }
+ controls[] =
+ {
+ {"Right Eye X", IDC_RIGHT_EYE_X},
+ {"Right Eye Y", IDC_RIGHT_EYE_Y},
+ {"Right Eye Z", IDC_RIGHT_EYE_Z},
+
+ {"Left Eye X", IDC_LEFT_EYE_X},
+ {"Left Eye Y", IDC_LEFT_EYE_Y},
+ {"Left Eye Z", IDC_LEFT_EYE_Z},
+
+ {"Upper Lid Raised", IDC_UPPER_LID_RAISED},
+ {"Upper Lid Neutral", IDC_UPPER_LID_NEUTRAL},
+ {"Upper Lid Lowered", IDC_UPPER_LID_LOWERED},
+
+ {"Lower Lid Raised", IDC_LOWER_LID_RAISED},
+ {"Lower Lid Neutral", IDC_LOWER_LID_NEUTRAL},
+ {"Lower Lid Lowered", IDC_LOWER_LID_LOWERED},
+
+ {"Upper Left Lid Raised", IDC_UPPER_LEFT_LID_RAISED},
+ {"Upper Left Lid Neutral", IDC_UPPER_LEFT_LID_NEUTRAL},
+ {"Upper Left Lid Lowered", IDC_UPPER_LEFT_LID_LOWERED},
+
+ {"Lower Left Lid Raised", IDC_LOWER_LEFT_LID_RAISED},
+ {"Lower Left Lid Neutral", IDC_LOWER_LEFT_LID_NEUTRAL},
+ {"Lower Left Lid Lowered", IDC_LOWER_LEFT_LID_LOWERED},
+
+ {"Iris Size", IDC_IRIS_SIZE},
+ {"Eyeball Size", IDC_EYEBALL_SIZE}
+ };
+
+ for ( int i=0; i < sizeof( controls ) / sizeof( controls[0] ); i++ )
+ {
+ char text[512];
+ GetDlgItem( controls[i].controlID )->GetWindowText( text, sizeof( text ) );
+
+ for ( int z=0; z < (int)strlen( text ); z++ )
+ {
+ if ( text[z] < '0' || text[z] > '9' )
+ {
+ if ( text[z] != '.' && text[z] != '-' )
+ {
+ char errMsg[512];
+ _snprintf( errMsg, sizeof( errMsg ), "The '%s' control must have a numeric value.", controls[i].pControlName );
+ AfxMessageBox( errMsg, MB_OK );
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+void CQC_EyesDlg::OnCreateQcText()
+{
+ if ( !CheckNumericInputs() )
+ return;
+
+ GenerateQCText();
+
+ // Clear the edit control.
+ LRESULT nLen = ::SendMessage( m_hOutputText, EM_GETLIMITTEXT, 0, 0 );
+ ::SendMessage( m_hOutputText, EM_SETSEL, 0, nLen );
+ ::SendMessage( m_hOutputText, EM_REPLACESEL, FALSE, (LPARAM)"" );
+
+ FormatAndSendToEditControl( m_hOutputText, m_Buf );
+
+ delete [] m_Buf;
+}
+
+void CQC_EyesDlg::OnIrisColorBrown()
+{
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_BROWN ), BM_SETCHECK, BST_CHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_GREEN ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_BLUE ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ SetupBitmapLabel( IDB_EYE_DEFAULT, "" );
+}
+
+void CQC_EyesDlg::OnIrisColorGreen()
+{
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_BROWN ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_GREEN ), BM_SETCHECK, BST_CHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_BLUE ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ SetupBitmapLabel( IDB_EYE_DEFAULT, "" );
+}
+
+void CQC_EyesDlg::OnIrisColorBlue()
+{
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_BROWN ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_GREEN ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_IRIS_COLOR_BLUE ), BM_SETCHECK, BST_CHECKED, 0 );
+ SetupBitmapLabel( IDB_EYE_DEFAULT, "" );
+}
+
+void CQC_EyesDlg::OnEyeColorDark()
+{
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_EYE_COLOR_LIGHT ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_EYE_COLOR_DARK ), BM_SETCHECK, BST_CHECKED, 0 );
+ SetupBitmapLabel( IDB_EYE_DEFAULT, "" );
+}
+
+void CQC_EyesDlg::OnEyeColorLight()
+{
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_EYE_COLOR_LIGHT ), BM_SETCHECK, BST_CHECKED, 0 );
+ ::SendMessage( ::GetDlgItem( m_hWnd, IDC_EYE_COLOR_DARK ), BM_SETCHECK, BST_UNCHECKED, 0 );
+ SetupBitmapLabel( IDB_EYE_DEFAULT, "" );
+}
+
+void CQC_EyesDlg::SetupBitmapLabel( UINT iBitmapResourceID, const char *pString, ... )
+{
+ char msg[4096];
+ va_list marker;
+ va_start( marker, pString );
+ _vsnprintf( msg, sizeof( msg ), pString, marker );
+ msg[ ARRAYSIZE(msg) - 1 ] = 0;
+ va_end( marker );
+
+ m_PictureControl.SetBitmap( GetCachedBitmap( iBitmapResourceID ) );
+ GetDlgItem( IDC_PICTURE_LABEL )->SetWindowText( msg );
+}
+
+void CQC_EyesDlg::OnSetfocusRightEyeX()
+{
+ SetupBitmapLabel( IDB_EYE_XY_R, "Enter the X position of the center vertex of the right eye" );
+}
+
+void CQC_EyesDlg::OnSetfocusRightEyeY()
+{
+ SetupBitmapLabel( IsOptionChecked( IDC_Y_AXIS_UP ) ? IDB_EYE_XY_R : IDB_EYE_Z_R, "Enter the Y position of the center vertex of the right eye" );
+}
+
+void CQC_EyesDlg::OnSetfocusRightEyeZ()
+{
+ SetupBitmapLabel( IsOptionChecked( IDC_Y_AXIS_UP ) ? IDB_EYE_Z_R : IDB_EYE_XY_R, "Enter the Z position of the center vertex of the right eye" );
+}
+
+void CQC_EyesDlg::OnSetfocusLeftEyeX()
+{
+ SetupBitmapLabel( IDB_EYE_XY_L, "Enter the X position of the center vertex of the right eye" );
+}
+
+void CQC_EyesDlg::OnSetfocusLeftEyeY()
+{
+ SetupBitmapLabel( IsOptionChecked( IDC_Y_AXIS_UP ) ? IDB_EYE_XY_L : IDB_EYE_Z_L, "Enter the Y position of the center vertex of the right eye" );
+}
+
+void CQC_EyesDlg::OnSetfocusLeftEyeZ()
+{
+ SetupBitmapLabel( IsOptionChecked( IDC_Y_AXIS_UP ) ? IDB_EYE_Z_L : IDB_EYE_XY_L, "Enter the Z position of the center vertex of the right eye" );
+}
+
+void CQC_EyesDlg::OnSetfocusUpperLidLowered()
+{
+ const char *pCoord = IsOptionChecked( IDC_Y_AXIS_UP ) ? "Y" : "Z";
+ SetupBitmapLabel( IDB_EYE_UPPER_LO, "At Frame 1, enter the %s position of the center vertex of the right upper eye lid", pCoord );
+}
+
+void CQC_EyesDlg::OnSetfocusUpperLidNeutral()
+{
+ const char *pCoord = IsOptionChecked( IDC_Y_AXIS_UP ) ? "Y" : "Z";
+ SetupBitmapLabel( IDB_EYE_UPPER_MID, "At Frame 0, enter the %s position of the center vertex of the right upper eye lid", pCoord );
+}
+
+void CQC_EyesDlg::OnSetfocusUpperLidRaised()
+{
+ const char *pCoord = IsOptionChecked( IDC_Y_AXIS_UP ) ? "Y" : "Z";
+ SetupBitmapLabel( IDB_EYE_UPPER_HI, "At Frame 2, enter the %s position of the center vertex of the right upper eye lid", pCoord );
+}
+
+
+void CQC_EyesDlg::OnSetfocusLowerLidLowered()
+{
+ const char *pCoord = IsOptionChecked( IDC_Y_AXIS_UP ) ? "Y" : "Z";
+ SetupBitmapLabel( IDB_EYE_LOWER_LO, "At Frame 3, enter the %s position of the center vertex of the right lower eye lid", pCoord );
+}
+
+void CQC_EyesDlg::OnSetfocusLowerLidNeutral()
+{
+ const char *pCoord = IsOptionChecked( IDC_Y_AXIS_UP ) ? "Y" : "Z";
+ SetupBitmapLabel( IDB_EYE_LOWER_MID, "At Frame 0, enter the %s position of the center vertex of the right lower eye lid", pCoord );
+}
+
+void CQC_EyesDlg::OnSetfocusLowerLidRaised()
+{
+ const char *pCoord = IsOptionChecked( IDC_Y_AXIS_UP ) ? "Y" : "Z";
+ SetupBitmapLabel( IDB_EYE_LOWER_HI, "At Frame 4, enter the %s position of the center vertex of the right lower eye lid", pCoord );
+}
+
+void CQC_EyesDlg::OnCopyTextToClipboard()
+{
+ if ( !CheckNumericInputs() )
+ return;
+
+ GenerateQCText();
+
+ if ( !OpenClipboard() )
+ return;
+
+ size_t textLen = strlen( m_Buf );
+ HANDLE hmem = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE, textLen + 1 );
+ if ( hmem )
+ {
+ void *ptr = GlobalLock( hmem );
+ if ( ptr )
+ {
+ memcpy( ptr, m_Buf, textLen+1 );
+ GlobalUnlock( hmem );
+
+ SetClipboardData( CF_TEXT, hmem );
+ }
+ }
+
+ CloseClipboard();
+
+ delete [] m_Buf;
+}
+
+
+int g_AdvancedControls[] =
+{
+ IDC_EYE_DETAIL_CONTROL_FRAME,
+ IDC_IRIS_SIZE,
+ IDC_IRIS_SIZE_LABEL,
+ IDC_EYEBALL_SIZE,
+ IDC_EYEBALL_SIZE_LABEL,
+ IDC_LEFT_LID_CONTROL
+};
+#define NUM_ADVANCED_CONTROLS ( sizeof( g_AdvancedControls ) / sizeof( g_AdvancedControls[0] ) )
+
+int g_LeftLidPositionControls[] =
+{
+ IDC_UPPER_LEFT_LID_PANEL,
+ IDC_UPPER_LEFT_LID_RAISED,
+ IDC_UPPER_LEFT_LID_RAISED_LABEL,
+ IDC_UPPER_LEFT_LID_NEUTRAL,
+ IDC_UPPER_LEFT_LID_NEUTRAL_LABEL,
+ IDC_UPPER_LEFT_LID_LOWERED,
+ IDC_UPPER_LEFT_LID_LOWERED_LABEL,
+ IDC_LOWER_LEFT_LID_PANEL,
+ IDC_LOWER_LEFT_LID_RAISED,
+ IDC_LOWER_LEFT_LID_RAISED_LABEL,
+ IDC_LOWER_LEFT_LID_NEUTRAL,
+ IDC_LOWER_LEFT_LID_NEUTRAL_LABEL,
+ IDC_LOWER_LEFT_LID_LOWERED,
+ IDC_LOWER_LEFT_LID_LOWERED_LABEL
+};
+#define NUM_LEFT_LID_POSITION_CONTROLS ( sizeof( g_LeftLidPositionControls ) / sizeof( g_LeftLidPositionControls[0] ) )
+
+
+void CQC_EyesDlg::OnDefaultControls()
+{
+ GetDlgItem( IDC_PICTURES )->ShowWindow( SW_SHOW );
+
+ // Hide all the advanced controls.
+ for ( int i=0; i < NUM_ADVANCED_CONTROLS; i++ )
+ {
+ GetDlgItem( g_AdvancedControls[i] )->ShowWindow( SW_HIDE );
+ }
+
+ for ( int i=0; i < NUM_LEFT_LID_POSITION_CONTROLS; i++ )
+ {
+ GetDlgItem( g_LeftLidPositionControls[i] )->ShowWindow( SW_HIDE );
+ }
+
+}
+
+void CQC_EyesDlg::OnAdvancedControls()
+{
+ GetDlgItem( IDC_PICTURES )->ShowWindow( SW_HIDE );
+
+ // Show the advanced controls.
+ for ( int i=0; i < NUM_ADVANCED_CONTROLS; i++ )
+ {
+ GetDlgItem( g_AdvancedControls[i] )->ShowWindow( SW_SHOW );
+ GetDlgItem( g_AdvancedControls[i] )->InvalidateRect( NULL );
+ }
+
+ if ( IsIndependentLeftLidControlEnabled() )
+ {
+ OnLeftLidControl();
+ }
+}
+
+
+bool CQC_EyesDlg::IsIndependentLeftLidControlEnabled()
+{
+ return m_IndependentLeftLidControl.GetCheck() == 1;
+}
+
+void CQC_EyesDlg::OnLeftLidControl()
+{
+ if ( IsIndependentLeftLidControlEnabled() )
+ {
+ for ( int i=0; i < NUM_LEFT_LID_POSITION_CONTROLS; i++ )
+ {
+ GetDlgItem( g_LeftLidPositionControls[i] )->ShowWindow( SW_SHOW );
+ GetDlgItem( g_LeftLidPositionControls[i] )->InvalidateRect( NULL );
+ }
+ }
+ else
+ {
+ for ( int i=0; i < NUM_LEFT_LID_POSITION_CONTROLS; i++ )
+ {
+ GetDlgItem( g_LeftLidPositionControls[i] )->ShowWindow( SW_HIDE );
+ GetDlgItem( g_LeftLidPositionControls[i] )->InvalidateRect( NULL );
+ }
+ }
+}
diff --git a/mp/src/utils/qc_eyes/QC_EyesDlg.h b/mp/src/utils/qc_eyes/QC_EyesDlg.h new file mode 100644 index 00000000..62b42b1a --- /dev/null +++ b/mp/src/utils/qc_eyes/QC_EyesDlg.h @@ -0,0 +1,134 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+// QC_EyesDlg.h : header file
+//
+
+#if !defined(AFX_QC_EYESDLG_H__9130E22D_05ED_4851_960C_38D90DA94967__INCLUDED_)
+#define AFX_QC_EYESDLG_H__9130E22D_05ED_4851_960C_38D90DA94967__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+class CDialogParams
+{
+public:
+ float m_flLeftEye[3];
+ float m_flRightEye[3];
+
+ float m_flIrisSize;
+ float m_flEyeballSize;
+
+ float m_flLeftUpperLidRaised;
+ float m_flLeftUpperLidNeutral;
+ float m_flLeftUpperLidLowered;
+
+ float m_flLeftLowerLidRaised;
+ float m_flLeftLowerLidNeutral;
+ float m_flLeftLowerLidLowered;
+
+ float m_flRightUpperLidRaised;
+ float m_flRightUpperLidNeutral;
+ float m_flRightUpperLidLowered;
+
+ float m_flRightLowerLidRaised;
+ float m_flRightLowerLidNeutral;
+ float m_flRightLowerLidLowered;
+
+ char m_ReferenceFilename[1024];
+ char m_ExpressionsFilename[1024];
+ char m_ModelFilename[1024];
+
+ char m_EyeballPrefix[1024]; // eyeball_ or dark_eyeball_
+ char m_PupilPrefix[1024]; // pupil_ or grn_pupil_ or bl_pupil_
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// CQC_EyesDlg dialog
+
+class CQC_EyesDlg : public CDialog
+{
+// Construction
+public:
+ CQC_EyesDlg(CWnd* pParent = NULL); // standard constructor
+
+// Dialog Data
+ //{{AFX_DATA(CQC_EyesDlg)
+ enum { IDD = IDD_QC_EYES_DIALOG };
+ CButton m_IndependentLeftLidControl;
+ CStatic m_PictureControl;
+ //}}AFX_DATA
+
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CQC_EyesDlg)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+ HICON m_hIcon;
+
+ void GenerateQCText();
+ void AddText( const char *pFormat, ... );
+ bool IsOptionChecked( UINT option );
+ float GetDlgItemFloat( UINT id );
+ void GetDialogParams( CDialogParams &p );
+ void SetupBitmapLabel( UINT iBitmapResourceID, const char *pString, ... );
+
+ HWND m_hOutputText;
+
+
+ // Cached list of bitmaps.
+ class CBitmapRef
+ {
+ public:
+ UINT m_iResource;
+ HBITMAP m_hBitmap;
+ CBitmapRef *m_pNext;
+ };
+ CBitmapRef *m_pBitmapHead;
+ HBITMAP GetCachedBitmap( UINT id );
+
+
+ size_t m_BufSize;
+ char *m_Buf;
+ bool IsIndependentLeftLidControlEnabled();
+
+ bool CheckNumericInputs();
+
+
+ // Generated message map functions
+ //{{AFX_MSG(CQC_EyesDlg)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnPaint();
+ afx_msg HCURSOR OnQueryDragIcon();
+ afx_msg void OnCreateQcText();
+ afx_msg void OnIrisColorBrown();
+ afx_msg void OnIrisColorGreen();
+ afx_msg void OnIrisColorBlue();
+ afx_msg void OnEyeColorDark();
+ afx_msg void OnEyeColorLight();
+ afx_msg void OnSetfocusRightEyeX();
+ afx_msg void OnSetfocusRightEyeY();
+ afx_msg void OnSetfocusRightEyeZ();
+ afx_msg void OnSetfocusLeftEyeX();
+ afx_msg void OnSetfocusLeftEyeY();
+ afx_msg void OnSetfocusLeftEyeZ();
+ afx_msg void OnSetfocusUpperLidLowered();
+ afx_msg void OnSetfocusUpperLidNeutral();
+ afx_msg void OnSetfocusUpperLidRaised();
+ afx_msg void OnSetfocusLowerLidLowered();
+ afx_msg void OnSetfocusLowerLidNeutral();
+ afx_msg void OnSetfocusLowerLidRaised();
+ afx_msg void OnCopyTextToClipboard();
+ afx_msg void OnDefaultControls();
+ afx_msg void OnAdvancedControls();
+ afx_msg void OnLeftLidControl();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_QC_EYESDLG_H__9130E22D_05ED_4851_960C_38D90DA94967__INCLUDED_)
diff --git a/mp/src/utils/qc_eyes/StdAfx.cpp b/mp/src/utils/qc_eyes/StdAfx.cpp new file mode 100644 index 00000000..8fdb97ce --- /dev/null +++ b/mp/src/utils/qc_eyes/StdAfx.cpp @@ -0,0 +1,9 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+// stdafx.cpp : source file that includes just the standard includes
+// QC_Eyes.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+
+
diff --git a/mp/src/utils/qc_eyes/StdAfx.h b/mp/src/utils/qc_eyes/StdAfx.h new file mode 100644 index 00000000..c65813d7 --- /dev/null +++ b/mp/src/utils/qc_eyes/StdAfx.h @@ -0,0 +1,28 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__88FA48B3_A92C_49CA_8A82_50D120A84756__INCLUDED_)
+#define AFX_STDAFX_H__88FA48B3_A92C_49CA_8A82_50D120A84756__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+#include <afxdisp.h> // MFC Automation classes
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__88FA48B3_A92C_49CA_8A82_50D120A84756__INCLUDED_)
diff --git a/mp/src/utils/qc_eyes/qc_eyes-2010.vcxproj b/mp/src/utils/qc_eyes/qc_eyes-2010.vcxproj new file mode 100644 index 00000000..0de210fb --- /dev/null +++ b/mp/src/utils/qc_eyes/qc_eyes-2010.vcxproj @@ -0,0 +1,261 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>QC_Eyes</ProjectName>
+ <ProjectGuid>{EA02FAE0-2A4F-C7C8-6176-5DEDA8E139E9}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>qc_eyes</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>qc_eyes</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 qc_eyes.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\qc_eyes;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>Debug/QC_Eyes.pch</PrecompiledHeaderOutputFile>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\qc_eyes.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/qc_eyes.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 qc_eyes.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\qc_eyes;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>Debug/QC_Eyes.pch</PrecompiledHeaderOutputFile>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\qc_eyes.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/qc_eyes.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="QC_Eyes.h" />
+ <ClInclude Include="QC_EyesDlg.h" />
+ <ClInclude Include="Resource.h" />
+ <ClInclude Include="StdAfx.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="QC_Eyes.cpp" />
+ <ClCompile Include="QC_EyesDlg.cpp" />
+ <ClCompile Include="StdAfx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="QC_Eyes.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\eye_default.bmp" />
+ <None Include="res\eye_lower_hi.bmp" />
+ <None Include="res\eye_lower_lo.bmp" />
+ <None Include="res\eye_lower_mid.bmp" />
+ <None Include="res\eye_upper_hi.bmp" />
+ <None Include="res\eye_upper_lo.bmp" />
+ <None Include="res\eye_upper_mid.bmp" />
+ <None Include="res\eye_XY_L.bmp" />
+ <None Include="res\eye_XY_R.bmp" />
+ <None Include="res\eye_Z_L.bmp" />
+ <None Include="res\eye_Z_R.bmp" />
+ <None Include="res\QC_Eyes.ico" />
+ <None Include="res\QC_Eyes.rc2" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/qc_eyes/qc_eyes-2010.vcxproj.filters b/mp/src/utils/qc_eyes/qc_eyes-2010.vcxproj.filters new file mode 100644 index 00000000..cdbb0778 --- /dev/null +++ b/mp/src/utils/qc_eyes/qc_eyes-2010.vcxproj.filters @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resources">
+ <UniqueIdentifier>{DDCF50C2-9294-D441-4F3F-7C1BBC892CB5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="QC_Eyes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="QC_EyesDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StdAfx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="QC_Eyes.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="QC_EyesDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StdAfx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="QC_Eyes.rc">
+ <Filter>Source Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="res\eye_default.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_lower_hi.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_lower_lo.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_lower_mid.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_upper_hi.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_upper_lo.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_upper_mid.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_XY_L.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_XY_R.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_Z_L.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\eye_Z_R.bmp">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\QC_Eyes.ico">
+ <Filter>Resources</Filter>
+ </None>
+ <None Include="res\QC_Eyes.rc2">
+ <Filter>Resources</Filter>
+ </None>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/qc_eyes/res/QC_Eyes.ico b/mp/src/utils/qc_eyes/res/QC_Eyes.ico Binary files differnew file mode 100644 index 00000000..7eef0bcb --- /dev/null +++ b/mp/src/utils/qc_eyes/res/QC_Eyes.ico diff --git a/mp/src/utils/qc_eyes/res/QC_Eyes.rc2 b/mp/src/utils/qc_eyes/res/QC_Eyes.rc2 new file mode 100644 index 00000000..b57420f8 --- /dev/null +++ b/mp/src/utils/qc_eyes/res/QC_Eyes.rc2 @@ -0,0 +1,13 @@ +//
+// QC_EYES.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/mp/src/utils/qc_eyes/res/eye_XY_L.bmp b/mp/src/utils/qc_eyes/res/eye_XY_L.bmp Binary files differnew file mode 100644 index 00000000..3350ed2f --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_XY_L.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_XY_R.bmp b/mp/src/utils/qc_eyes/res/eye_XY_R.bmp Binary files differnew file mode 100644 index 00000000..9bfbc91f --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_XY_R.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_Z_L.bmp b/mp/src/utils/qc_eyes/res/eye_Z_L.bmp Binary files differnew file mode 100644 index 00000000..949f9ac1 --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_Z_L.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_Z_R.bmp b/mp/src/utils/qc_eyes/res/eye_Z_R.bmp Binary files differnew file mode 100644 index 00000000..72042e1d --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_Z_R.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_default.bmp b/mp/src/utils/qc_eyes/res/eye_default.bmp Binary files differnew file mode 100644 index 00000000..705ac95b --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_default.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_lower_hi.bmp b/mp/src/utils/qc_eyes/res/eye_lower_hi.bmp Binary files differnew file mode 100644 index 00000000..9a88d59e --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_lower_hi.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_lower_lo.bmp b/mp/src/utils/qc_eyes/res/eye_lower_lo.bmp Binary files differnew file mode 100644 index 00000000..90b42b60 --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_lower_lo.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_lower_mid.bmp b/mp/src/utils/qc_eyes/res/eye_lower_mid.bmp Binary files differnew file mode 100644 index 00000000..9affa705 --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_lower_mid.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_upper_hi.bmp b/mp/src/utils/qc_eyes/res/eye_upper_hi.bmp Binary files differnew file mode 100644 index 00000000..fdc8b38e --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_upper_hi.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_upper_lo.bmp b/mp/src/utils/qc_eyes/res/eye_upper_lo.bmp Binary files differnew file mode 100644 index 00000000..16313d58 --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_upper_lo.bmp diff --git a/mp/src/utils/qc_eyes/res/eye_upper_mid.bmp b/mp/src/utils/qc_eyes/res/eye_upper_mid.bmp Binary files differnew file mode 100644 index 00000000..724ede4a --- /dev/null +++ b/mp/src/utils/qc_eyes/res/eye_upper_mid.bmp diff --git a/mp/src/utils/qc_eyes/resource.h b/mp/src/utils/qc_eyes/resource.h new file mode 100644 index 00000000..d16935a9 --- /dev/null +++ b/mp/src/utils/qc_eyes/resource.h @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by QC_Eyes.rc
+//
+#define IDD_QC_EYES_DIALOG 102
+#define IDR_MAINFRAME 128
+#define IDB_EYE_DEFAULT 150
+#define IDB_EYE_LOWER_HI 151
+#define IDB_EYE_LOWER_LO 152
+#define IDB_EYE_LOWER_MID 153
+#define IDB_EYE_UPPER_HI 154
+#define IDB_EYE_UPPER_LO 155
+#define IDB_EYE_UPPER_MID 156
+#define IDB_EYE_XY_L 157
+#define IDB_EYE_XY_R 158
+#define IDB_EYE_Z_L 159
+#define IDB_EYE_Z_R 160
+#define IDC_REFERENCE_FILENAME 1000
+#define IDC_EXPRESSIONS_FILENAME 1001
+#define IDC_MODEL_FILENAME 1002
+#define IDC_RIGHT_EYE_X 1003
+#define IDC_RIGHT_EYE_Y 1004
+#define IDC_RIGHT_EYE_Z 1005
+#define IDC_LEFT_EYE_X 1006
+#define IDC_LEFT_EYE_Y 1007
+#define IDC_LEFT_EYE_Z 1008
+#define IDC_Y_AXIS_UP 1009
+#define IDC_Z_AXIS_UP 1010
+#define IDC_DEFAULT_CONTROLS 1011
+#define IDC_ADVANCED_CONTROLS 1012
+#define IDC_UPPER_LID_RAISED 1013
+#define IDC_OUTPUT_TEXT 1014
+#define IDC_UPPER_LEFT_LID_RAISED 1015
+#define IDC_UPPER_LID_LOWERED 1016
+#define IDC_UPPER_LID_NEUTRAL 1017
+#define IDC_LOWER_LID_RAISED 1018
+#define IDC_LOWER_LID_LOWERED 1019
+#define IDC_LOWER_LID_NEUTRAL 1020
+#define IDC_IRIS_COLOR_GREEN 1021
+#define IDC_IRIS_COLOR_BLUE 1022
+#define IDC_IRIS_COLOR_BROWN 1023
+#define IDC_EYE_COLOR_LIGHT 1024
+#define IDC_EYE_COLOR_DARK 1025
+#define IDC_IRIS_SIZE 1026
+#define IDC_CREATE_QC_TEXT 1027
+#define IDC_EYEBALL_SIZE 1028
+#define IDC_COPY_TEXT_TO_CLIPBOARD 1029
+#define IDC_PICTURES 1030
+#define IDC_PICTURE_LABEL 1031
+#define IDC_LEFT_LID_CONTROL 1032
+#define IDC_UPPER_LEFT_LID_NEUTRAL 1033
+#define IDC_UPPER_LEFT_LID_LOWERED 1034
+#define IDC_LOWER_LEFT_LID_RAISED 1035
+#define IDC_LOWER_LEFT_LID_NEUTRAL 1036
+#define IDC_LOWER_LEFT_LID_LOWERED 1037
+#define IDC_EYE_DETAIL_CONTROL_FRAME 1038
+#define IDC_IRIS_SIZE_LABEL 1039
+#define IDC_EYEBALL_SIZE_LABEL 1040
+#define IDC_UPPER_LEFT_LID_PANEL 1041
+#define IDC_UPPER_LEFT_LID_RAISED_LABEL 1042
+#define IDC_UPPER_LEFT_LID_NEUTRAL_LABEL 1043
+#define IDC_UPPER_LEFT_LID_LOWERED_LABEL 1044
+#define IDC_LOWER_LEFT_LID_PANEL 1045
+#define IDC_LOWER_LEFT_LID_RAISED_LABEL 1046
+#define IDC_LOWER_LEFT_LID_NEUTRAL_LABEL 1047
+#define IDC_LOWER_LEFT_LID_LOWERED_LABEL 1048
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 130
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1049
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/mp/src/utils/serverplugin_sample/serverplugin_bot.cpp b/mp/src/utils/serverplugin_sample/serverplugin_bot.cpp new file mode 100644 index 00000000..250bf149 --- /dev/null +++ b/mp/src/utils/serverplugin_sample/serverplugin_bot.cpp @@ -0,0 +1,383 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Basic BOT handling.
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "interface.h"
+#include "filesystem.h"
+#undef VECTOR_NO_SLOW_OPERATIONS
+#include "mathlib/vector.h"
+
+#include "eiface.h"
+#include "edict.h"
+#include "game/server/iplayerinfo.h"
+#include "igameevents.h"
+#include "convar.h"
+#include "vstdlib/random.h"
+#include "../../game/shared/in_buttons.h"
+#include "../../game/shared/shareddefs.h"
+//#include "../../game_shared/util_shared.h"
+#include "engine/IEngineTrace.h"
+
+extern IBotManager *botmanager;
+extern IUniformRandomStream *randomStr;
+extern IPlayerInfoManager *playerinfomanager;
+extern IVEngineServer *engine;
+extern IEngineTrace *enginetrace;
+extern IPlayerInfoManager *playerinfomanager; // game dll interface to interact with players
+extern IServerPluginHelpers *helpers; // special 3rd party plugin helpers from the engine
+
+extern CGlobalVars *gpGlobals;
+
+ConVar bot_forcefireweapon( "plugin_bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." );
+ConVar bot_forceattack2( "plugin_bot_forceattack2", "0", 0, "When firing, use attack2." );
+ConVar bot_forceattackon( "plugin_bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." );
+ConVar bot_flipout( "plugin_bot_flipout", "0", 0, "When on, all bots fire their guns." );
+ConVar bot_changeclass( "plugin_bot_changeclass", "0", 0, "Force all bots to change to the specified class." );
+static ConVar bot_mimic( "plugin_bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
+static ConVar bot_mimic_yaw_offset( "plugin_bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw." );
+
+ConVar bot_sendcmd( "plugin_bot_sendcmd", "", 0, "Forces bots to send the specified command." );
+ConVar bot_crouch( "plugin_bot_crouch", "0", 0, "Bot crouches" );
+
+
+// This is our bot class.
+class CPluginBot
+{
+public:
+ CPluginBot() :
+ m_bBackwards(0),
+ m_flNextTurnTime(0),
+ m_bLastTurnToRight(0),
+ m_flNextStrafeTime(0),
+ m_flSideMove(0),
+ m_ForwardAngle(),
+ m_LastAngles()
+ {
+ }
+
+ bool m_bBackwards;
+
+ float m_flNextTurnTime;
+ bool m_bLastTurnToRight;
+
+ float m_flNextStrafeTime;
+ float m_flSideMove;
+
+ QAngle m_ForwardAngle;
+ QAngle m_LastAngles;
+
+ IBotController *m_BotInterface;
+ IPlayerInfo *m_PlayerInfo;
+ edict_t *m_BotEdict;
+};
+
+CUtlVector<CPluginBot> s_Bots;
+
+void Bot_Think( CPluginBot *pBot );
+
+// Handler for the "bot" command.
+void BotAdd_f()
+{
+ if ( !botmanager )
+ return;
+
+ static int s_BotNum = 0;
+ char botName[64];
+ Q_snprintf( botName, sizeof(botName), "Bot_%i", s_BotNum );
+ s_BotNum++;
+
+ edict_t *botEdict = botmanager->CreateBot( botName );
+ if ( botEdict )
+ {
+ int botIndex = s_Bots.AddToTail();
+ CPluginBot & bot = s_Bots[ botIndex ];
+ bot.m_BotInterface = botmanager->GetBotController( botEdict );
+ bot.m_PlayerInfo = playerinfomanager->GetPlayerInfo( botEdict );
+ bot.m_BotEdict = botEdict;
+ Assert( bot.m_BotInterface );
+ }
+}
+
+ConCommand cc_Bot( "plugin_bot_add", BotAdd_f, "Add a bot." );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Run through all the Bots in the game and let them think.
+//-----------------------------------------------------------------------------
+void Bot_RunAll( void )
+{
+ if ( !botmanager )
+ return;
+
+ for ( int i = 0; i < s_Bots.Count(); i++ )
+ {
+ CPluginBot & bot = s_Bots[i];
+ if ( bot.m_BotEdict->IsFree() || !bot.m_BotEdict->GetUnknown()|| !bot.m_PlayerInfo->IsConnected() )
+ {
+ s_Bots.Remove(i);
+ --i;
+ }
+ else
+ {
+ Bot_Think( &bot );
+ }
+ }
+}
+
+bool Bot_RunMimicCommand( CBotCmd& cmd )
+{
+ if ( bot_mimic.GetInt() <= 0 )
+ return false;
+
+ if ( bot_mimic.GetInt() > gpGlobals->maxClients )
+ return false;
+
+ IPlayerInfo *playerInfo = playerinfomanager->GetPlayerInfo( engine->PEntityOfEntIndex( bot_mimic.GetInt() ) );
+ if ( !playerInfo )
+ return false;
+
+ cmd = playerInfo->GetLastUserCommand();
+ cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
+
+ if( bot_crouch.GetInt() )
+ cmd.buttons |= IN_DUCK;
+
+ return true;
+}
+
+void Bot_UpdateStrafing( CPluginBot *pBot, CBotCmd &cmd )
+{
+ if ( gpGlobals->curtime >= pBot->m_flNextStrafeTime )
+ {
+ pBot->m_flNextStrafeTime = gpGlobals->curtime + 1.0f;
+
+ if ( randomStr->RandomInt( 0, 5 ) == 0 )
+ {
+ pBot->m_flSideMove = -600.0f + 1200.0f * randomStr->RandomFloat( 0, 2 );
+ }
+ else
+ {
+ pBot->m_flSideMove = 0;
+ }
+ cmd.sidemove = pBot->m_flSideMove;
+
+ if ( randomStr->RandomInt( 0, 20 ) == 0 )
+ {
+ pBot->m_bBackwards = true;
+ }
+ else
+ {
+ pBot->m_bBackwards = false;
+ }
+ }
+}
+
+void Bot_UpdateDirection( CPluginBot *pBot )
+{
+ float angledelta = 15.0;
+
+ int maxtries = (int)360.0/angledelta;
+
+ if ( pBot->m_bLastTurnToRight )
+ {
+ angledelta = -angledelta;
+ }
+
+ QAngle angle( pBot->m_BotInterface->GetLocalAngles() );
+
+ trace_t trace;
+ Vector vecSrc, vecEnd, forward;
+ while ( --maxtries >= 0 )
+ {
+ AngleVectors( angle, &forward );
+
+ vecSrc = pBot->m_BotInterface->GetLocalOrigin() + Vector( 0, 0, 36 );
+ vecEnd = vecSrc + forward * 10;
+
+ Ray_t ray;
+ ray.Init( vecSrc, vecEnd, Vector(-16, -16, 0 ), Vector( 16, 16, 72 ) );
+ CTraceFilterWorldAndPropsOnly traceFilter;
+ enginetrace->TraceRay( ray, MASK_PLAYERSOLID, &traceFilter, &trace );
+
+ if ( trace.fraction == 1.0 )
+ {
+ if ( gpGlobals->curtime < pBot->m_flNextTurnTime )
+ {
+ break;
+ }
+ }
+
+ angle.y += angledelta;
+
+ if ( angle.y > 180 )
+ angle.y -= 360;
+ else if ( angle.y < -180 )
+ angle.y += 360;
+
+ pBot->m_flNextTurnTime = gpGlobals->curtime + 2.0;
+ pBot->m_bLastTurnToRight = randomStr->RandomInt( 0, 1 ) == 0 ? true : false;
+
+ pBot->m_ForwardAngle = angle;
+ pBot->m_LastAngles = angle;
+ }
+
+ pBot->m_BotInterface->SetLocalAngles( angle );
+}
+
+
+void Bot_FlipOut( CPluginBot *pBot, CBotCmd &cmd )
+{
+ if ( bot_flipout.GetInt() > 0 && !pBot->m_PlayerInfo->IsDead() )
+ {
+ if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
+ {
+ cmd.buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
+ }
+
+ if ( bot_flipout.GetInt() >= 2 )
+ {
+ QAngle angOffset = RandomAngle( -1, 1 );
+
+ pBot->m_LastAngles += angOffset;
+
+ for ( int i = 0 ; i < 2; i++ )
+ {
+ if ( fabs( pBot->m_LastAngles[ i ] - pBot->m_ForwardAngle[ i ] ) > 15.0f )
+ {
+ if ( pBot->m_LastAngles[ i ] > pBot->m_ForwardAngle[ i ] )
+ {
+ pBot->m_LastAngles[ i ] = pBot->m_ForwardAngle[ i ] + 15;
+ }
+ else
+ {
+ pBot->m_LastAngles[ i ] = pBot->m_ForwardAngle[ i ] - 15;
+ }
+ }
+ }
+
+ pBot->m_LastAngles[ 2 ] = 0;
+
+ pBot->m_BotInterface->SetLocalAngles( pBot->m_LastAngles );
+ }
+ }
+}
+
+
+void Bot_HandleSendCmd( CPluginBot *pBot )
+{
+ if ( strlen( bot_sendcmd.GetString() ) > 0 )
+ {
+ //send the cmd from this bot
+ helpers->ClientCommand( pBot->m_BotEdict, bot_sendcmd.GetString() );
+
+ bot_sendcmd.SetValue("");
+ }
+}
+
+
+// If bots are being forced to fire a weapon, see if I have it
+void Bot_ForceFireWeapon( CPluginBot *pBot, CBotCmd &cmd )
+{
+ if ( Q_strlen( bot_forcefireweapon.GetString() ) > 0 )
+ {
+ pBot->m_BotInterface->SetActiveWeapon( bot_forcefireweapon.GetString() );
+ bot_forcefireweapon.SetValue( "" );
+ // Start firing
+ // Some weapons require releases, so randomise firing
+ if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
+ {
+ cmd.buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
+ }
+ }
+}
+
+
+void Bot_SetForwardMovement( CPluginBot *pBot, CBotCmd &cmd )
+{
+ if ( !pBot->m_BotInterface->IsEFlagSet(EFL_BOT_FROZEN) )
+ {
+ if ( pBot->m_PlayerInfo->GetHealth() == 100 )
+ {
+ cmd.forwardmove = 600 * ( pBot->m_bBackwards ? -1 : 1 );
+ if ( pBot->m_flSideMove != 0.0f )
+ {
+ cmd.forwardmove *= randomStr->RandomFloat( 0.1, 1.0f );
+ }
+ }
+ else
+ {
+ // Stop when shot
+ cmd.forwardmove = 0;
+ }
+ }
+}
+
+
+void Bot_HandleRespawn( CPluginBot *pBot, CBotCmd &cmd )
+{
+ // Wait for Reinforcement wave
+ if ( pBot->m_PlayerInfo->IsDead() )
+ {
+ if ( pBot->m_PlayerInfo->GetTeamIndex() == 0 )
+ {
+ helpers->ClientCommand( pBot->m_BotEdict, "joingame" );
+ helpers->ClientCommand( pBot->m_BotEdict, "jointeam 3" );
+ helpers->ClientCommand( pBot->m_BotEdict, "joinclass 0" );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Run this Bot's AI for one frame.
+//-----------------------------------------------------------------------------
+void Bot_Think( CPluginBot *pBot )
+{
+ CBotCmd cmd;
+ Q_memset( &cmd, 0, sizeof( cmd ) );
+
+ // Finally, override all this stuff if the bot is being forced to mimic a player.
+ if ( !Bot_RunMimicCommand( cmd ) )
+ {
+ cmd.sidemove = pBot->m_flSideMove;
+
+ if ( !pBot->m_PlayerInfo->IsDead() )
+ {
+ Bot_SetForwardMovement( pBot, cmd );
+
+ // Only turn if I haven't been hurt
+ if ( !pBot->m_BotInterface->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_PlayerInfo->GetHealth() == 100 )
+ {
+ Bot_UpdateDirection( pBot );
+ Bot_UpdateStrafing( pBot, cmd );
+ }
+
+ // Handle console settings.
+ Bot_ForceFireWeapon( pBot, cmd );
+ Bot_HandleSendCmd( pBot );
+ }
+ else
+ {
+ Bot_HandleRespawn( pBot, cmd );
+ }
+
+ Bot_FlipOut( pBot, cmd );
+
+ cmd.viewangles = pBot->m_BotInterface->GetLocalAngles();
+ cmd.upmove = 0;
+ cmd.impulse = 0;
+ }
+
+ pBot->m_BotInterface->RunPlayerMove( &cmd );
+}
+
+
diff --git a/mp/src/utils/serverplugin_sample/serverplugin_empty-2010.vcxproj b/mp/src/utils/serverplugin_sample/serverplugin_empty-2010.vcxproj new file mode 100644 index 00000000..14a96c78 --- /dev/null +++ b/mp/src/utils/serverplugin_sample/serverplugin_empty-2010.vcxproj @@ -0,0 +1,258 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Serverplugin_empty</ProjectName>
+ <ProjectGuid>{394B82B6-3999-E576-5458-2D2EB4229509}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>serverplugin_empty</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>serverplugin_empty</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\..\game\server;..\..\game\shared</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=serverplugin_empty;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;serverplugin_emptyONLY;_MBCS;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\serverplugin_sample;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);odbc32.lib;odbccp32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\serverplugin_empty.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/serverplugin_empty.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\..\game\server;..\..\game\shared</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=serverplugin_empty;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;serverplugin_emptyONLY;_MBCS;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\serverplugin_sample;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);odbc32.lib;odbccp32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\serverplugin_empty.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/serverplugin_empty.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier0\basetypes.h" />
+ <ClInclude Include="..\..\public\Color.h" />
+ <ClInclude Include="..\..\public\tier0\dbg.h" />
+ <ClInclude Include="..\..\public\eiface.h" />
+ <ClInclude Include="..\..\public\filesystem.h" />
+ <ClInclude Include="..\..\public\tier0\icommandline.h" />
+ <ClInclude Include="..\..\public\igameevents.h" />
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="..\..\public\game\server\iplayerinfo.h" />
+ <ClInclude Include="..\..\public\engine\iserverplugin.h" />
+ <ClInclude Include="..\..\public\tier1\KeyValues.h" />
+ <ClInclude Include="..\..\public\tier0\mem.h" />
+ <ClInclude Include="..\..\public\tier0\memalloc.h" />
+ <ClInclude Include="..\..\public\tier0\memdbgon.h" />
+ <ClInclude Include="..\..\public\tier1\strtools.h" />
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\tier1\utlvector.h" />
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="serverplugin_bot.cpp" />
+ <ClCompile Include="serverplugin_empty.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/serverplugin_sample/serverplugin_empty-2010.vcxproj.filters b/mp/src/utils/serverplugin_sample/serverplugin_empty-2010.vcxproj.filters new file mode 100644 index 00000000..7f979a47 --- /dev/null +++ b/mp/src/utils/serverplugin_sample/serverplugin_empty-2010.vcxproj.filters @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier0\basetypes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\Color.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\dbg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\eiface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\icommandline.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\igameevents.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\game\server\iplayerinfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\engine\iserverplugin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\KeyValues.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\mem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memalloc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memdbgon.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\strtools.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlvector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="serverplugin_bot.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="serverplugin_empty.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/serverplugin_sample/serverplugin_empty.cpp b/mp/src/utils/serverplugin_sample/serverplugin_empty.cpp new file mode 100644 index 00000000..ff82b5f6 --- /dev/null +++ b/mp/src/utils/serverplugin_sample/serverplugin_empty.cpp @@ -0,0 +1,922 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+
+#include <stdio.h>
+
+//#define GAME_DLL
+#ifdef GAME_DLL
+#include "cbase.h"
+#endif
+
+#include <stdio.h>
+#include "interface.h"
+#include "filesystem.h"
+#include "engine/iserverplugin.h"
+#include "eiface.h"
+#include "igameevents.h"
+#include "convar.h"
+#include "Color.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineTrace.h"
+#include "tier2/tier2.h"
+#include "game/server/pluginvariant.h"
+#include "game/server/iplayerinfo.h"
+#include "game/server/ientityinfo.h"
+#include "game/server/igameinfo.h"
+
+//#define SAMPLE_TF2_PLUGIN
+#ifdef SAMPLE_TF2_PLUGIN
+#include "tf/tf_shareddefs.h"
+#endif
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Interfaces from the engine
+IVEngineServer *engine = NULL; // helper functions (messaging clients, loading content, making entities, running commands, etc)
+IGameEventManager *gameeventmanager_ = NULL; // game events interface
+#ifndef GAME_DLL
+#define gameeventmanager gameeventmanager_
+#endif
+IPlayerInfoManager *playerinfomanager = NULL; // game dll interface to interact with players
+IEntityInfoManager *entityinfomanager = NULL; // game dll interface to interact with all entities (like IPlayerInfo)
+IGameInfoManager *gameinfomanager = NULL; // game dll interface to get data from game rules directly
+IBotManager *botmanager = NULL; // game dll interface to interact with bots
+IServerPluginHelpers *helpers = NULL; // special 3rd party plugin helpers from the engine
+IUniformRandomStream *randomStr = NULL;
+IEngineTrace *enginetrace = NULL;
+
+
+CGlobalVars *gpGlobals = NULL;
+
+// function to initialize any cvars/command in this plugin
+void Bot_RunAll( void );
+
+// useful helper func
+#ifndef GAME_DLL
+inline bool FStrEq(const char *sz1, const char *sz2)
+{
+ return(Q_stricmp(sz1, sz2) == 0);
+}
+#endif
+//---------------------------------------------------------------------------------
+// Purpose: a sample 3rd party plugin class
+//---------------------------------------------------------------------------------
+class CEmptyServerPlugin: public IServerPluginCallbacks, public IGameEventListener
+{
+public:
+ CEmptyServerPlugin();
+ ~CEmptyServerPlugin();
+
+ // IServerPluginCallbacks methods
+ virtual bool Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory );
+ virtual void Unload( void );
+ virtual void Pause( void );
+ virtual void UnPause( void );
+ virtual const char *GetPluginDescription( void );
+ virtual void LevelInit( char const *pMapName );
+ virtual void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax );
+ virtual void GameFrame( bool simulating );
+ virtual void LevelShutdown( void );
+ virtual void ClientActive( edict_t *pEntity );
+ virtual void ClientDisconnect( edict_t *pEntity );
+ virtual void ClientPutInServer( edict_t *pEntity, char const *playername );
+ virtual void SetCommandClient( int index );
+ virtual void ClientSettingsChanged( edict_t *pEdict );
+ virtual PLUGIN_RESULT ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen );
+ virtual PLUGIN_RESULT ClientCommand( edict_t *pEntity, const CCommand &args );
+ virtual PLUGIN_RESULT NetworkIDValidated( const char *pszUserName, const char *pszNetworkID );
+ virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue );
+ virtual void OnEdictAllocated( edict_t *edict );
+ virtual void OnEdictFreed( const edict_t *edict );
+
+ // IGameEventListener Interface
+ virtual void FireGameEvent( KeyValues * event );
+
+ virtual int GetCommandIndex() { return m_iClientCommandIndex; }
+private:
+ int m_iClientCommandIndex;
+};
+
+
+//
+// The plugin is a static singleton that is exported as an interface
+//
+CEmptyServerPlugin g_EmtpyServerPlugin;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CEmptyServerPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_EmtpyServerPlugin );
+
+//---------------------------------------------------------------------------------
+// Purpose: constructor/destructor
+//---------------------------------------------------------------------------------
+CEmptyServerPlugin::CEmptyServerPlugin()
+{
+ m_iClientCommandIndex = 0;
+}
+
+CEmptyServerPlugin::~CEmptyServerPlugin()
+{
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when the plugin is loaded, load the interface we need from the engine
+//---------------------------------------------------------------------------------
+bool CEmptyServerPlugin::Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory )
+{
+ ConnectTier1Libraries( &interfaceFactory, 1 );
+ ConnectTier2Libraries( &interfaceFactory, 1 );
+
+ entityinfomanager = (IEntityInfoManager *)gameServerFactory(INTERFACEVERSION_ENTITYINFOMANAGER,NULL);
+ if ( !entityinfomanager )
+ {
+ Warning( "Unable to load entityinfomanager, ignoring\n" ); // this isn't fatal, we just won't be able to access entity data
+ }
+
+ playerinfomanager = (IPlayerInfoManager *)gameServerFactory(INTERFACEVERSION_PLAYERINFOMANAGER,NULL);
+ if ( !playerinfomanager )
+ {
+ Warning( "Unable to load playerinfomanager, ignoring\n" ); // this isn't fatal, we just won't be able to access specific player data
+ }
+
+ botmanager = (IBotManager *)gameServerFactory(INTERFACEVERSION_PLAYERBOTMANAGER, NULL);
+ if ( !botmanager )
+ {
+ Warning( "Unable to load botcontroller, ignoring\n" ); // this isn't fatal, we just won't be able to access specific bot functions
+ }
+ gameinfomanager = (IGameInfoManager *)gameServerFactory(INTERFACEVERSION_GAMEINFOMANAGER, NULL);
+ if (!gameinfomanager)
+ {
+ Warning( "Unable to load gameinfomanager, ignoring\n" );
+ }
+
+ engine = (IVEngineServer*)interfaceFactory(INTERFACEVERSION_VENGINESERVER, NULL);
+ gameeventmanager = (IGameEventManager *)interfaceFactory(INTERFACEVERSION_GAMEEVENTSMANAGER,NULL);
+ helpers = (IServerPluginHelpers*)interfaceFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL);
+ enginetrace = (IEngineTrace *)interfaceFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL);
+ randomStr = (IUniformRandomStream *)interfaceFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL);
+
+ // get the interfaces we want to use
+ if( ! ( engine && gameeventmanager && g_pFullFileSystem && helpers && enginetrace && randomStr ) )
+ {
+ return false; // we require all these interface to function
+ }
+
+ if ( playerinfomanager )
+ {
+ gpGlobals = playerinfomanager->GetGlobalVars();
+ }
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+ ConVar_Register( 0 );
+ return true;
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when the plugin is unloaded (turned off)
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::Unload( void )
+{
+ gameeventmanager->RemoveListener( this ); // make sure we are unloaded from the event system
+
+ ConVar_Unregister( );
+ DisconnectTier2Libraries( );
+ DisconnectTier1Libraries( );
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when the plugin is paused (i.e should stop running but isn't unloaded)
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::Pause( void )
+{
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when the plugin is unpaused (i.e should start executing again)
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::UnPause( void )
+{
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: the name of this plugin, returned in "plugin_print" command
+//---------------------------------------------------------------------------------
+const char *CEmptyServerPlugin::GetPluginDescription( void )
+{
+ return "Emtpy-Plugin V2, Valve";
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called on level start
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::LevelInit( char const *pMapName )
+{
+ Msg( "Level \"%s\" has been loaded\n", pMapName );
+ gameeventmanager->AddListener( this, true );
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called on level start, when the server is ready to accept client connections
+// edictCount is the number of entities in the level, clientMax is the max client count
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
+{
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called once per server frame, do recurring work here (like checking for timeouts)
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::GameFrame( bool simulating )
+{
+ if ( simulating )
+ {
+ Bot_RunAll();
+ }
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called on level end (as the server is shutting down or going to a new map)
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::LevelShutdown( void ) // !!!!this can get called multiple times per map change
+{
+ gameeventmanager->RemoveListener( this );
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when a client spawns into a server (i.e as they begin to play)
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::ClientActive( edict_t *pEntity )
+{
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when a client leaves a server (or is timed out)
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::ClientDisconnect( edict_t *pEntity )
+{
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called on
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::ClientPutInServer( edict_t *pEntity, char const *playername )
+{
+ KeyValues *kv = new KeyValues( "msg" );
+ kv->SetString( "title", "Hello" );
+ kv->SetString( "msg", "Hello there" );
+ kv->SetColor( "color", Color( 255, 0, 0, 255 ));
+ kv->SetInt( "level", 5);
+ kv->SetInt( "time", 10);
+ helpers->CreateMessage( pEntity, DIALOG_MSG, kv, this );
+ kv->deleteThis();
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called on level start
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::SetCommandClient( int index )
+{
+ m_iClientCommandIndex = index;
+}
+
+void ClientPrint( edict_t *pEdict, char *format, ... )
+{
+ va_list argptr;
+ static char string[1024];
+
+ va_start (argptr, format);
+ Q_vsnprintf(string, sizeof(string), format,argptr);
+ va_end (argptr);
+
+ engine->ClientPrintf( pEdict, string );
+}
+//---------------------------------------------------------------------------------
+// Purpose: called on level start
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::ClientSettingsChanged( edict_t *pEdict )
+{
+ if ( playerinfomanager )
+ {
+ IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEdict );
+
+ const char * name = engine->GetClientConVarValue( engine->IndexOfEdict(pEdict), "name" );
+
+ // CAN'T use Q_stricmp here, this dll is made by 3rd parties and may not link to tier0/vstdlib
+ if ( playerinfo && name && playerinfo->GetName() &&
+ stricmp( name, playerinfo->GetName()) ) // playerinfo may be NULL if the MOD doesn't support access to player data
+ // OR if you are accessing the player before they are fully connected
+ {
+ ClientPrint( pEdict, "Your name changed to \"%s\" (from \"%s\"\n", name, playerinfo->GetName() );
+ // this is the bad way to check this, the better option it to listen for the "player_changename" event in FireGameEvent()
+ // this is here to give a real example of how to use the playerinfo interface
+ }
+ }
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when a client joins a server
+//---------------------------------------------------------------------------------
+PLUGIN_RESULT CEmptyServerPlugin::ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
+{
+ return PLUGIN_CONTINUE;
+}
+
+CON_COMMAND( DoAskConnect, "Server plugin example of using the ask connect dialog" )
+{
+ if ( args.ArgC() < 2 )
+ {
+ Warning ( "DoAskConnect <server IP>\n" );
+ }
+ else
+ {
+ const char *pServerIP = args.Arg( 1 );
+
+ KeyValues *kv = new KeyValues( "menu" );
+ kv->SetString( "title", pServerIP ); // The IP address of the server to connect to goes in the "title" field.
+ kv->SetInt( "time", 3 );
+
+ for ( int i=1; i < gpGlobals->maxClients; i++ )
+ {
+ edict_t *pEdict = engine->PEntityOfEntIndex( i );
+ if ( pEdict )
+ {
+ helpers->CreateMessage( pEdict, DIALOG_ASKCONNECT, kv, &g_EmtpyServerPlugin );
+ }
+ }
+
+ kv->deleteThis();
+ }
+}
+
+#ifdef SAMPLE_TF2_PLUGIN
+const char *classNames[] =
+{
+ "unknown",
+ "scout",
+ "sniper",
+ "soldier",
+ "demoman",
+ "medic",
+ "heavy weapons guy",
+ "pyro",
+ "spy",
+ "engineer",
+};
+
+bool TFPlayerHasCondition( int inBits, int condition )
+{
+ Assert( condition >= 0 && condition < TF_COND_LAST );
+
+ return ( ( inBits & (1<<condition) ) != 0 );
+}
+
+void SentryStatus( edict_t *pEntity )
+{
+ IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
+ if (!playerinfo)
+ {
+ Msg("couldn't get playerinfo\n");
+ return;
+ }
+
+ Msg("Sentry Status:\n");
+ pluginvariant value;
+ pluginvariant emptyVariant;
+ edict_t *pSentry = NULL;
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_SENTRY, value, emptyVariant))
+ {
+ pSentry = engine->PEntityOfEntIndex( value.Int() );
+ if (!pSentry)
+ {
+ Warning("couldn't attain sentry gun entity\n");
+ return;
+ }
+ }
+ else
+ {
+ Msg("No Sentrygun built.\n");
+ return;
+
+ }
+ IEntityInfo *entinfo = entityinfomanager->GetEntityInfo( pSentry );
+ if (!entinfo)
+ {
+ Warning("couldn't get entinfo for sentry gun\n");
+ return;
+ }
+
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_SENTRY, value, emptyVariant))
+ {
+ if (value.Bool())
+ Msg("Sentry Under Construction...\n");
+ }
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_UPGRADING_SENTRY, value, emptyVariant))
+ {
+ if (value.Bool())
+ Msg("Sentry Upgrading...\n");
+ }
+
+ int sentryLevel = 0;
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_LEVEL, value, emptyVariant))
+ {
+ sentryLevel = value.Int();
+ Msg("Sentry Level: %i\n", sentryLevel );
+ }
+ else
+ Msg("Unable to retrive sentry level\n");
+
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_PROGRESS, value, emptyVariant))
+ {
+ if (sentryLevel < 3)
+ {
+ int iMetal, iRequiredMetal;
+ iRequiredMetal = value.Int() & 0xFF;
+ iMetal = (value.Int()>>8) & 0xFF;
+ Msg("%i / %i Metal Required for Sentry Level %i\n", iMetal, iRequiredMetal, sentryLevel+1);
+ }
+ else
+ Msg("Sentry cannot be upgraded further.\n");
+ }
+
+ Msg("Health: %i\n", entinfo->GetHealth() );
+
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_KILLS, value, emptyVariant))
+ Msg("Kills: %i\n", value.Int() );
+ else
+ Msg("Unable to retrieve sentry kills\n");
+
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_AMMO_SHELLS, value, emptyVariant))
+ {
+ int iShells, iMaxShells;
+ iMaxShells = value.Int() & 0xFF;
+ iShells = (value.Int()>>8) & 0xFF;
+ Msg("Shells: %i / %i\n", iShells, iMaxShells);
+ }
+ if (sentryLevel > 2)
+ {
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_AMMO_ROCKETS, value, emptyVariant))
+ {
+ int iRockets, iMaxRockets;
+ iMaxRockets = value.Int() & 0xFF;
+ iRockets = (value.Int()>>8) & 0xFF;
+ Msg("Rockets: %i / %i\n", iRockets, iMaxRockets);
+ }
+ }
+
+}
+void DispenserStatus( edict_t *pEntity )
+{
+ IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
+ if (!playerinfo)
+ {
+ Msg("couldn't get playerinfo\n");
+ return;
+ }
+
+ Msg("Dispenser Status:\n");
+ pluginvariant value;
+ pluginvariant emptyVariant;
+ edict_t *pDispenser = NULL;
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_DISPENSER, value, emptyVariant))
+ {
+ pDispenser = engine->PEntityOfEntIndex( value.Int() );
+ if (!pDispenser)
+ {
+ Warning("couldn't attain dispenser entity\n");
+ return;
+ }
+ }
+ else
+ {
+ Msg("No dispenser built.\n");
+ return;
+ }
+ IEntityInfo *entinfo = entityinfomanager->GetEntityInfo( pDispenser );
+ if (!entinfo)
+ {
+ Warning("couldn't get entinfo for dispenser\n");
+ return;
+ }
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_DISPENSER, value, emptyVariant))
+ {
+ if (value.Bool())
+ Msg("Dispenser Under Construction...\n");
+ }
+ Msg("Health: %i\n", entinfo->GetHealth() );
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_DISPENSER_METAL, value, emptyVariant))
+ Msg("Metal: %i\n", value.Int() );
+}
+void TeleporterStatus( edict_t *pEntity )
+{
+ IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
+ if (!playerinfo)
+ {
+ Msg("couldn't get playerinfo\n");
+ return;
+ }
+
+ Msg("Teleporter Status:\n");
+
+ pluginvariant value;
+ pluginvariant emptyVariant;
+ edict_t *pEntrance = NULL;
+ edict_t *pExit = NULL;
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_TELEPORTER_ENTRANCE, value, emptyVariant))
+ {
+ pEntrance = engine->PEntityOfEntIndex( value.Int() );
+ if (!pEntrance)
+ {
+ Warning("couldn't attain entrance entity\n");
+ }
+ }
+ else
+ {
+ Msg("No Teleporter Entrance built.\n");
+ }
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_TELEPORTER_EXIT, value, emptyVariant))
+ {
+ pExit = engine->PEntityOfEntIndex( value.Int() );
+ if (!pExit)
+ {
+ Warning("couldn't attain exit entity\n");
+ }
+ }
+ else
+ {
+ Msg("No Teleporter Entrance built.\n");
+ }
+ IEntityInfo *entranceInfo = entityinfomanager->GetEntityInfo( pEntrance );
+ if (!entranceInfo)
+ {
+ Warning("couldn't get entinfo for teleporter entrance\n");
+ }
+ IEntityInfo *exitInfo = entityinfomanager->GetEntityInfo( pExit );
+ if (!exitInfo)
+ {
+ Warning("couldn't get entinfo for teleporter exit\n");
+ }
+
+ if (pEntrance && entranceInfo)
+ {
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_TELEPORTER_ENTRANCE, value, emptyVariant))
+ {
+ if (value.Bool())
+ Msg("Entrance Under Construction...\n");
+ }
+ Msg("Entrance Health: %i\n", entranceInfo->GetHealth() );
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_TELEPORTER_USES, value, emptyVariant))
+ Msg("Entrance Used %i Times.\n", value.Int() );
+
+ }
+ if (pExit && exitInfo)
+ {
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_TELEPORTER_EXIT, value, emptyVariant))
+ {
+ if (value.Bool())
+ Msg("Exit Under Construction...\n");
+ }
+ Msg("Exit Health: %i\n", exitInfo->GetHealth() );
+ }
+}
+void ClassStatus( edict_t *pEntity )
+{
+ IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
+ if (!playerinfo)
+ {
+ Msg("couldn't get playerinfo\n");
+ return;
+ }
+ int playerClassId = playerinfo->GetPlayerClassId();
+
+ Msg("Player Class: %s\n", playerinfo->GetPlayerClassName());
+ pluginvariant conditionValue;
+ pluginvariant emptyVariant;
+ if (!playerinfo->GetCustomInfo(TFPLAYERINFO_CONDITIONS, conditionValue, emptyVariant))
+ {
+ Warning("unable to retrieve conditions!\n");
+ }
+ if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_INVULNERABLE ))
+ Msg("You are Invulnerable!\n");
+ if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_SELECTED_TO_TELEPORT ))
+ Msg("You are about to Teleport.\n");
+ if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_TELEPORTED ))
+ Msg("You have recently been teleported.\n");
+
+ switch(playerClassId)
+ {
+ default:
+ case TF_CLASS_MEDIC:
+ break;
+ case TF_CLASS_ENGINEER:
+ Msg("Building Information:\n");
+ SentryStatus( pEntity );
+ DispenserStatus( pEntity );
+ TeleporterStatus( pEntity );
+ break;
+ case TF_CLASS_SPY:
+ {
+ int disguiseClass = 0;
+ pluginvariant value;
+
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_SPY_DISGUISEDAS, value, emptyVariant))
+ disguiseClass = value.Int();
+
+ if ( TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISING ) )
+ Msg("Disguising..\n");
+ else if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISED ) )
+ Msg("Disguised as: %s\n", classNames[disguiseClass] );
+
+ if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_STEALTHED ))
+ Msg("Cloaked!\n");
+ if (playerinfo->GetCustomInfo(TFPLAYERINFO_SPY_CLOAKCHARGELEVEL, value, emptyVariant))
+ Msg("Cloak Charge Percent: %d\n", value.Float() );
+
+ break;
+ }
+ case TF_CLASS_DEMOMAN:
+ break;
+ }
+}
+const char *ctf_flagtype[] =
+{
+ "ctf", //TF_FLAGTYPE_CTF = 0,
+ "attack / defend", //TF_FLAGTYPE_ATTACK_DEFEND,
+ "territory control", //TF_FLAGTYPE_TERRITORY_CONTROL,
+ "invade", //TF_FLAGTYPE_INVADE,
+ "king of the hill", //TF_FLAGTYPE_KINGOFTHEHILL,
+};
+const char *ctf_flagstatus[] =
+{
+ "unknown",
+ "At Home",
+ "Dropped",
+ "Stolen",
+};
+void FlagStatus( edict_t *pPlayer )
+{
+ IPlayerInfo *pInfo = playerinfomanager->GetPlayerInfo( pPlayer );
+ if (!pInfo)
+ {
+ Msg( "couldn't get playerinfo\n" );
+ return;
+ }
+ IGameInfo *gameInfo = gameinfomanager->GetGameInfo();
+ if (!gameInfo)
+ {
+ Msg( "couldn't get gameinfo\n" );
+ }
+
+ int gameType = gameInfo->GetInfo_GameType();
+
+ if (gameType != 1)
+ {
+ Msg( "Game is not CTF.\n" );
+ return;
+ }
+ Msg( "===============================\n" );
+ Msg( "Capture The Flag -- Flag Status\n" );
+ Msg( "===============================\n" );
+ pluginvariant value, options;
+
+ edict_t *pFlag = NULL;
+ while ( (pFlag = entityinfomanager->FindEntityByClassname(pFlag, "item_teamflag")) != NULL )
+ {
+ IEntityInfo *pFlagInfo = entityinfomanager->GetEntityInfo( pFlag );
+ if (!pFlagInfo)
+ continue;
+
+ Msg( "\nTeam %s's Flag\n", gameInfo->GetInfo_GetTeamName( pFlagInfo->GetTeamIndex() ) );
+ options.SetInt(engine->IndexOfEdict(pFlag));
+ if ( gameInfo->GetInfo_Custom( TFGAMEINFO_CTF_FLAG_TYPE, value, options) )
+ Msg( "Type: %s\n", ctf_flagtype[value.Int()] );
+ if ( gameInfo->GetInfo_Custom( TFGAMEINFO_CTF_FLAG_STATUS, value, options) )
+ {
+ Msg( "Status: %s\n", ctf_flagstatus[value.Int()] );
+ //Tony; if we're carried, find out who has us.
+ if (value.Int() == 3)
+ {
+ edict_t *pPlayer = pFlagInfo->GetOwner();
+ if (pPlayer)
+ {
+ IPlayerInfo *pPlayerInfo = playerinfomanager->GetPlayerInfo( pPlayer );
+ if (pPlayerInfo)
+ Msg( "Carried by: %s\n", pPlayerInfo->GetName() );
+ }
+ }
+ }
+ }
+
+
+ Msg( "===============================\n" );
+}
+#endif
+
+//---------------------------------------------------------------------------------
+// Purpose: called when a client types in a command (only a subset of commands however, not CON_COMMAND's)
+//---------------------------------------------------------------------------------
+PLUGIN_RESULT CEmptyServerPlugin::ClientCommand( edict_t *pEntity, const CCommand &args )
+{
+ const char *pcmd = args[0];
+
+ if ( !pEntity || pEntity->IsFree() )
+ {
+ return PLUGIN_CONTINUE;
+ }
+
+ if ( FStrEq( pcmd, "menu" ) )
+ {
+ KeyValues *kv = new KeyValues( "menu" );
+ kv->SetString( "title", "You've got options, hit ESC" );
+ kv->SetInt( "level", 1 );
+ kv->SetColor( "color", Color( 255, 0, 0, 255 ));
+ kv->SetInt( "time", 20 );
+ kv->SetString( "msg", "Pick an option\nOr don't." );
+
+ for( int i = 1; i < 9; i++ )
+ {
+ char num[10], msg[10], cmd[10];
+ Q_snprintf( num, sizeof(num), "%i", i );
+ Q_snprintf( msg, sizeof(msg), "Option %i", i );
+ Q_snprintf( cmd, sizeof(cmd), "option%i", i );
+
+ KeyValues *item1 = kv->FindKey( num, true );
+ item1->SetString( "msg", msg );
+ item1->SetString( "command", cmd );
+ }
+
+ helpers->CreateMessage( pEntity, DIALOG_MENU, kv, this );
+ kv->deleteThis();
+ return PLUGIN_STOP; // we handled this function
+ }
+ else if ( FStrEq( pcmd, "rich" ) )
+ {
+ KeyValues *kv = new KeyValues( "menu" );
+ kv->SetString( "title", "A rich message" );
+ kv->SetInt( "level", 1 );
+ kv->SetInt( "time", 20 );
+ kv->SetString( "msg", "This is a long long long text string.\n\nIt also has line breaks." );
+
+ helpers->CreateMessage( pEntity, DIALOG_TEXT, kv, this );
+ kv->deleteThis();
+ return PLUGIN_STOP; // we handled this function
+ }
+ else if ( FStrEq( pcmd, "msg" ) )
+ {
+ KeyValues *kv = new KeyValues( "menu" );
+ kv->SetString( "title", "Just a simple hello" );
+ kv->SetInt( "level", 1 );
+ kv->SetInt( "time", 20 );
+
+ helpers->CreateMessage( pEntity, DIALOG_MSG, kv, this );
+ kv->deleteThis();
+ return PLUGIN_STOP; // we handled this function
+ }
+ else if ( FStrEq( pcmd, "entry" ) )
+ {
+ KeyValues *kv = new KeyValues( "entry" );
+ kv->SetString( "title", "Stuff" );
+ kv->SetString( "msg", "Enter something" );
+ kv->SetString( "command", "say" ); // anything they enter into the dialog turns into a say command
+ kv->SetInt( "level", 1 );
+ kv->SetInt( "time", 20 );
+
+ helpers->CreateMessage( pEntity, DIALOG_ENTRY, kv, this );
+ kv->deleteThis();
+ return PLUGIN_STOP; // we handled this function
+ }
+#ifdef SAMPLE_TF2_PLUGIN
+ else if ( FStrEq( pcmd, "gameinfo" ) )
+ {
+ IGameInfo *gameInfo = gameinfomanager->GetGameInfo();
+ if (!gameInfo)
+ return PLUGIN_STOP;
+
+ Msg("=== Game Information ===\n");
+ Msg("Game Type: %i / %s\n", gameInfo->GetInfo_GameType(), gameInfo->GetInfo_GameTypeName() );
+ int teamCount = gameInfo->GetInfo_GetTeamCount();
+ Msg("Num Teams: %i\n", teamCount );
+
+ Msg("Player Counts:\n");
+ for (int i = 0;i<teamCount;i++)
+ {
+ //If this failes, we can assume the rest is invalid too.
+ if (!gameInfo->GetInfo_GetTeamName(i) )
+ continue;
+ Msg("Team: %s, Players: %i\n", gameInfo->GetInfo_GetTeamName(i), gameInfo->GetInfo_NumPlayersOnTeam(i) );
+ }
+ return PLUGIN_STOP;
+
+ }
+ // Sample to use the new CustomInfo added to TF2 for plugins
+ else if ( FStrEq( pcmd, "tfcond" ) )
+ {
+ IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
+ if (!playerinfo)
+ return PLUGIN_STOP;
+
+ pluginvariant conditionValue;
+ pluginvariant emptyVariant;
+ if (!playerinfo->GetCustomInfo(TFPLAYERINFO_CONDITIONS, conditionValue, emptyVariant))
+ {
+ Msg("unable to retrieve conditions!\n");
+ return PLUGIN_STOP;
+ }
+
+ Msg("Disguising?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISING ) ? "yes" : "no" );
+ Msg("Disguised?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISED ) ? "yes" : "no" );
+ Msg("Stealthed?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_STEALTHED ) ? "yes" : "no" );
+ Msg("Invulnerable?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_INVULNERABLE ) ? "yes" : "no" );
+ Msg("Teleported Recently?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_TELEPORTED ) ? "yes" : "no" );
+ Msg("Selected for Teleportation?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_SELECTED_TO_TELEPORT ) ? "yes" : "no" );
+ Msg("On Fire?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_BURNING ) ? "yes" : "no" );
+
+ return PLUGIN_STOP;
+ }
+ else if ( FStrEq( pcmd, "sentry_status" ) )
+ {
+ SentryStatus(pEntity);
+ return PLUGIN_STOP;
+ }
+ else if ( FStrEq( pcmd, "class_status" ) )
+ {
+ ClassStatus(pEntity);
+ return PLUGIN_STOP;
+ }
+ else if ( FStrEq( pcmd, "flag_status" ) )
+ {
+ FlagStatus(pEntity);
+ return PLUGIN_STOP;
+ }
+ #ifdef GAME_DLL
+ else if ( FStrEq( pcmd, "cbe_test" ) )
+ {
+ IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
+ if (!playerinfo)
+ return PLUGIN_STOP;
+
+ CBaseEntity *pEnt = static_cast< CBaseEntity* >(entityinfomanager->GetEntity( pEntity ));
+ if (pEnt)
+ Msg("got a pointer to CBaseEntity..\n");
+ Msg("attempting to print this entities modelname directly..\n");
+
+ Msg("ModelName: %s\n", STRING(pEnt->GetModelName()) );
+
+ return PLUGIN_STOP;
+ }
+ #endif
+#endif
+
+
+ return PLUGIN_CONTINUE;
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when a client is authenticated
+//---------------------------------------------------------------------------------
+PLUGIN_RESULT CEmptyServerPlugin::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID )
+{
+ return PLUGIN_CONTINUE;
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when a cvar value query is finished
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
+{
+ Msg( "Cvar query (cookie: %d, status: %d) - name: %s, value: %s\n", iCookie, eStatus, pCvarName, pCvarValue );
+}
+void CEmptyServerPlugin::OnEdictAllocated( edict_t *edict )
+{
+}
+void CEmptyServerPlugin::OnEdictFreed( const edict_t *edict )
+{
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: called when an event is fired
+//---------------------------------------------------------------------------------
+void CEmptyServerPlugin::FireGameEvent( KeyValues * event )
+{
+ const char * name = event->GetName();
+ Msg( "CEmptyServerPlugin::FireGameEvent: Got event \"%s\"\n", name );
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: an example of how to implement a new command
+//---------------------------------------------------------------------------------
+CON_COMMAND( empty_version, "prints the version of the empty plugin" )
+{
+ Msg( "Version:2.0.0.0\n" );
+}
+
+CON_COMMAND( empty_log, "logs the version of the empty plugin" )
+{
+ engine->LogPrint( "Version:2.0.0.0\n" );
+}
+
+//---------------------------------------------------------------------------------
+// Purpose: an example cvar
+//---------------------------------------------------------------------------------
+static ConVar empty_cvar("plugin_empty", "0", FCVAR_NOTIFY, "Example plugin cvar");
diff --git a/mp/src/utils/smdlexp/smdlexp.cpp b/mp/src/utils/smdlexp/smdlexp.cpp new file mode 100644 index 00000000..fafb5da6 --- /dev/null +++ b/mp/src/utils/smdlexp/smdlexp.cpp @@ -0,0 +1,1096 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "MAX.H"
+#include "DECOMP.H"
+#include "STDMAT.H"
+#include "ANIMTBL.H"
+#include "istdplug.h"
+#include "phyexp.h"
+#include "BonesPro.h"
+
+#include "smexprc.h"
+#include "smedefs.h"
+
+//===================================================================
+// Prototype declarations
+//
+int GetIndexOfINode(INode *pnode,BOOL fAssertPropExists = TRUE);
+void SetIndexOfINode(INode *pnode, int inode);
+BOOL FUndesirableNode(INode *pnode);
+BOOL FNodeMarkedToSkip(INode *pnode);
+float FlReduceRotation(float fl);
+
+
+//===================================================================
+// Global variable definitions
+//
+
+// Save for use with dialogs
+static HINSTANCE hInstance;
+
+// We just need one of these to hand off to 3DSMAX.
+static SmdExportClassDesc SmdExportCD;
+
+// For OutputDebugString and misc sprintf's
+static char st_szDBG[300];
+
+// INode mapping table
+static int g_inmMac = 0;
+
+//===================================================================
+// Utility functions
+//
+
+static int AssertFailedFunc(char *sz)
+{
+ MessageBox(GetActiveWindow(), sz, "Assert failure", MB_OK);
+ int Set_Your_Breakpoint_Here = 1;
+ return 1;
+}
+#define ASSERT_MBOX(f, sz) ((f) ? 1 : AssertFailedFunc(sz))
+
+
+//===================================================================
+// Required plug-in export functions
+//
+BOOL WINAPI DllMain( HINSTANCE hinstDLL, ULONG fdwReason, LPVOID lpvReserved)
+{
+ static int fFirstTimeHere = TRUE;
+ if (fFirstTimeHere)
+ {
+ fFirstTimeHere = FALSE;
+ hInstance = hinstDLL;
+ }
+ return TRUE;
+}
+
+
+EXPORT_THIS int LibNumberClasses(void)
+{
+ return 1;
+}
+
+
+EXPORT_THIS ClassDesc *LibClassDesc(int iWhichClass)
+{
+ switch(iWhichClass)
+ {
+ case 0: return &SmdExportCD;
+ default: return 0;
+ }
+}
+
+
+EXPORT_THIS const TCHAR *LibDescription()
+{
+ return _T("Valve SMD Plug-in.");
+}
+
+
+EXPORT_THIS ULONG LibVersion()
+{
+ return VERSION_3DSMAX;
+}
+
+
+//=====================================================================
+// Methods for SmdExportClass
+//
+
+CONSTRUCTOR SmdExportClass::SmdExportClass(void)
+{
+ m_rgmaxnode = NULL;
+}
+
+
+DESTRUCTOR SmdExportClass::~SmdExportClass(void)
+{
+ if (m_rgmaxnode)
+ delete[] m_rgmaxnode;
+}
+
+
+int SmdExportClass::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options)
+{
+ ExpInterface *pexpiface = ei; // Hungarian
+ Interface *piface = i; // Hungarian
+
+ // Reset the name-map property manager
+ g_inmMac = 0;
+
+ if (!suppressPrompts)
+ {
+ // Present the user with the Export Options dialog
+ if (DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_EXPORTOPTIONS), GetActiveWindow(),
+ ExportOptionsDlgProc, (LPARAM)this) <= 0)
+ return 0; // error or cancel
+ }
+ else
+ {
+ m_fReferenceFrame = 0;
+ }
+
+ // Break up filename, re-assemble longer versions
+ TSTR strPath, strFile, strExt;
+ TCHAR szFile[MAX_PATH];
+ SplitFilename(TSTR(name), &strPath, &strFile, &strExt);
+ sprintf(szFile, "%s\\%s.%s", (char*)strPath, (char*)strFile, DEFAULT_EXT);
+
+ /*
+ if (m_fReferenceFrame)
+ sprintf(szFile, "%s\\%s_model.%s", (char*)strPath, (char*)strFile, DEFAULT_EXT);
+ */
+
+ FILE *pFile;
+ if ((pFile = fopen(szFile, "w")) == NULL)
+ return FALSE/*failure*/;
+
+ fprintf( pFile, "version %d\n", 1 );
+
+ // Get animation metrics
+ m_intervalOfAnimation = piface->GetAnimRange();
+ m_tvStart = m_intervalOfAnimation.Start();
+ m_tvEnd = m_intervalOfAnimation.End();
+ m_tpf = ::GetTicksPerFrame();
+
+ // Count nodes, label them, collect into array
+ if (!CollectNodes(pexpiface))
+ return 0; /*fail*/
+
+ // Output nodes
+ if (!DumpBones(pFile, pexpiface))
+ {
+ fclose( pFile );
+ return 0; /*fail*/
+ }
+
+ // Output bone rotations, for each frame. Do only first frame if this is the reference frame MAX file
+ DumpRotations(pFile, pexpiface);
+
+ // Output triangle meshes (first frame/all frames), if this is the reference frame MAX file
+ if (m_fReferenceFrame)
+ {
+ DumpModel(pFile, pexpiface);
+ }
+
+ if (!suppressPrompts)
+ {
+ // Tell user that exporting is finished (it can take a while with no feedback)
+ char szExportComplete[300];
+ sprintf(szExportComplete, "Exported %s.", szFile);
+ MessageBox(GetActiveWindow(), szExportComplete, "Status", MB_OK);
+ }
+
+ fclose( pFile );
+
+ return 1/*success*/;
+}
+
+
+BOOL SmdExportClass::CollectNodes( ExpInterface *pexpiface)
+{
+ // Count total nodes in the model, so I can alloc array
+ // Also "brands" each node with node index, or with "skip me" marker.
+ CountNodesTEP procCountNodes;
+ procCountNodes.m_cNodes = 0;
+ (void) pexpiface->theScene->EnumTree(&procCountNodes);
+ ASSERT_MBOX(procCountNodes.m_cNodes > 0, "No nodes!");
+
+ // Alloc and fill array
+ m_imaxnodeMac = procCountNodes.m_cNodes;
+ m_rgmaxnode = new MaxNode[m_imaxnodeMac];
+ ASSERT_MBOX(m_rgmaxnode != NULL, "new failed");
+
+
+ CollectNodesTEP procCollectNodes;
+ procCollectNodes.m_phec = this;
+ (void) pexpiface->theScene->EnumTree(&procCollectNodes);
+
+ return TRUE;
+}
+
+
+BOOL SmdExportClass::DumpBones(FILE *pFile, ExpInterface *pexpiface)
+{
+ // Dump bone names
+ DumpNodesTEP procDumpNodes;
+ procDumpNodes.m_pfile = pFile;
+ procDumpNodes.m_phec = this;
+ fprintf(pFile, "nodes\n" );
+ (void) pexpiface->theScene->EnumTree(&procDumpNodes);
+ fprintf(pFile, "end\n" );
+
+ return TRUE;
+}
+
+
+BOOL SmdExportClass::DumpRotations(FILE *pFile, ExpInterface *pexpiface)
+{
+ // Dump bone-rotation info, for each frame
+ // Also dumps root-node translation info (the model's world-position at each frame)
+ DumpFrameRotationsTEP procDumpFrameRotations;
+ procDumpFrameRotations.m_pfile = pFile;
+ procDumpFrameRotations.m_phec = this;
+
+ TimeValue m_tvTill = (m_fReferenceFrame) ? m_tvStart : m_tvEnd;
+
+ fprintf(pFile, "skeleton\n" );
+ for (TimeValue tv = m_tvStart; tv <= m_tvTill; tv += m_tpf)
+ {
+ fprintf(pFile, "time %d\n", tv / GetTicksPerFrame() );
+ procDumpFrameRotations.m_tvToDump = tv;
+ (void) pexpiface->theScene->EnumTree(&procDumpFrameRotations);
+ }
+ fprintf(pFile, "end\n" );
+
+ return TRUE;
+}
+
+
+BOOL SmdExportClass::DumpModel( FILE *pFile, ExpInterface *pexpiface)
+{
+ // Dump mesh info: vertices, normals, UV texture map coords, bone assignments
+ DumpModelTEP procDumpModel;
+ procDumpModel.m_pfile = pFile;
+ procDumpModel.m_phec = this;
+ fprintf(pFile, "triangles\n" );
+ procDumpModel.m_tvToDump = m_tvStart;
+ (void) pexpiface->theScene->EnumTree(&procDumpModel);
+ fprintf(pFile, "end\n" );
+ return TRUE;
+}
+
+
+
+
+//=============================================================================
+// TREE-ENUMERATION PROCEDURES
+//=============================================================================
+
+#define ASSERT_AND_ABORT(f, sz) \
+ if (!(f)) \
+ { \
+ ASSERT_MBOX(FALSE, sz); \
+ cleanup( ); \
+ return TREE_ABORT; \
+ }
+
+
+//=================================================================
+// Methods for CountNodesTEP
+//
+int CountNodesTEP::callback( INode *node)
+{
+ INode *pnode = node; // Hungarian
+
+ ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
+
+ if (::FUndesirableNode(pnode))
+ {
+ // Mark as skippable
+ ::SetIndexOfINode(pnode, SmdExportClass::UNDESIRABLE_NODE_MARKER);
+ return TREE_CONTINUE;
+ }
+
+ // Establish "node index"--just ascending ints
+ ::SetIndexOfINode(pnode, m_cNodes);
+
+ m_cNodes++;
+
+ return TREE_CONTINUE;
+}
+
+
+//=================================================================
+// Methods for CollectNodesTEP
+//
+int CollectNodesTEP::callback(INode *node)
+{
+ INode *pnode = node; // Hungarian
+
+ ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
+
+ if (::FNodeMarkedToSkip(pnode))
+ return TREE_CONTINUE;
+
+ // Get pre-stored "index"
+ int iNode = ::GetIndexOfINode(pnode);
+ ASSERT_MBOX(iNode >= 0 && iNode <= m_phec->m_imaxnodeMac-1, "Bogus iNode");
+
+ // Get name, store name in array
+ TSTR strNodeName(pnode->GetName());
+ strcpy(m_phec->m_rgmaxnode[iNode].szNodeName, (char*)strNodeName);
+
+ // Get Node's time-zero Transformation Matrices
+ m_phec->m_rgmaxnode[iNode].mat3NodeTM = pnode->GetNodeTM(0/*TimeValue*/);
+ m_phec->m_rgmaxnode[iNode].mat3ObjectTM = pnode->GetObjectTM(0/*TimeValue*/);
+
+ // I'll calculate this later
+ m_phec->m_rgmaxnode[iNode].imaxnodeParent = SmdExportClass::UNDESIRABLE_NODE_MARKER;
+
+ return TREE_CONTINUE;
+}
+
+
+
+
+
+
+//=================================================================
+// Methods for DumpNodesTEP
+//
+int DumpNodesTEP::callback(INode *pnode)
+{
+ ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
+
+ if (::FNodeMarkedToSkip(pnode))
+ return TREE_CONTINUE;
+
+ // Get node's parent
+ INode *pnodeParent;
+ pnodeParent = pnode->GetParentNode();
+
+ // The model's root is a child of the real "scene root"
+ TSTR strNodeName(pnode->GetName());
+ BOOL fNodeIsRoot = pnodeParent->IsRootNode( );
+
+ int iNode = ::GetIndexOfINode(pnode);
+ int iNodeParent = ::GetIndexOfINode(pnodeParent, !fNodeIsRoot/*fAssertPropExists*/);
+
+ // Convenient time to cache this
+ m_phec->m_rgmaxnode[iNode].imaxnodeParent = fNodeIsRoot ? SmdExportClass::UNDESIRABLE_NODE_MARKER : iNodeParent;
+
+ // Root node has no parent, thus no translation
+ if (fNodeIsRoot)
+ iNodeParent = -1;
+
+ // check to see if the matrix isn't right handed
+ m_phec->m_rgmaxnode[iNode].isMirrored = DotProd( CrossProd( m_phec->m_rgmaxnode[iNode].mat3ObjectTM.GetRow(0).Normalize(), m_phec->m_rgmaxnode[iNode].mat3ObjectTM.GetRow(1).Normalize() ).Normalize(), m_phec->m_rgmaxnode[iNode].mat3ObjectTM.GetRow(2).Normalize() ) < 0;
+
+ // Dump node description
+ fprintf(m_pfile, "%3d \"%s\" %3d\n",
+ iNode,
+ strNodeName,
+ iNodeParent );
+
+ return TREE_CONTINUE;
+}
+
+
+
+//=================================================================
+// Methods for DumpFrameRotationsTEP
+//
+int DumpFrameRotationsTEP::callback(INode *pnode)
+{
+ ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
+
+ if (::FNodeMarkedToSkip(pnode))
+ return TREE_CONTINUE;
+
+ int iNode = ::GetIndexOfINode(pnode);
+
+ TSTR strNodeName(pnode->GetName());
+
+ // The model's root is a child of the real "scene root"
+ INode *pnodeParent = pnode->GetParentNode();
+ BOOL fNodeIsRoot = pnodeParent->IsRootNode( );
+
+ // Get Node's "Local" Transformation Matrix
+ Matrix3 mat3NodeTM = pnode->GetNodeTM(m_tvToDump);
+ Matrix3 mat3ParentTM = pnodeParent->GetNodeTM(m_tvToDump);
+ mat3NodeTM.NoScale(); // Clear these out because they apparently
+ mat3ParentTM.NoScale(); // screw up the following calculation.
+ Matrix3 mat3NodeLocalTM = mat3NodeTM * Inverse(mat3ParentTM);
+ Point3 rowTrans = mat3NodeLocalTM.GetTrans();
+
+ // check to see if the parent bone was mirrored. If so, mirror invert this bones position
+ if (m_phec->m_rgmaxnode[iNode].imaxnodeParent >= 0 && m_phec->m_rgmaxnode[m_phec->m_rgmaxnode[iNode].imaxnodeParent].isMirrored)
+ {
+ rowTrans = rowTrans * -1.0f;
+ }
+
+ // Get the rotation (via decomposition into "affine parts", then quaternion-to-Euler)
+ // Apparently the order of rotations returned by QuatToEuler() is X, then Y, then Z.
+ AffineParts affparts;
+ float rgflXYZRotations[3];
+
+ decomp_affine(mat3NodeLocalTM, &affparts);
+ QuatToEuler(affparts.q, rgflXYZRotations);
+
+ float xRot = rgflXYZRotations[0]; // in radians
+ float yRot = rgflXYZRotations[1]; // in radians
+ float zRot = rgflXYZRotations[2]; // in radians
+
+ // Get rotations in the -2pi...2pi range
+ xRot = ::FlReduceRotation(xRot);
+ yRot = ::FlReduceRotation(yRot);
+ zRot = ::FlReduceRotation(zRot);
+
+ // Print rotations
+ fprintf(m_pfile, "%3d %f %f %f %f %f %f\n",
+ // Node:%-15s Rotation (x,y,z)\n",
+ iNode, rowTrans.x, rowTrans.y, rowTrans.z, xRot, yRot, zRot);
+
+ return TREE_CONTINUE;
+}
+
+
+
+//=================================================================
+// Methods for DumpModelTEP
+//
+Modifier *FindPhysiqueModifier (INode *nodePtr)
+{
+ // Get object from node. Abort if no object.
+ Object *ObjectPtr = nodePtr->GetObjectRef();
+ if (!ObjectPtr) return NULL;
+
+ // Is derived object ?
+ if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
+ {
+ // Yes -> Cast.
+ IDerivedObject *DerivedObjectPtr = static_cast<IDerivedObject*>(ObjectPtr);
+
+ // Iterate over all entries of the modifier stack.
+ int ModStackIndex = 0;
+ while (ModStackIndex < DerivedObjectPtr->NumModifiers())
+ {
+ // Get current modifier.
+ Modifier *ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
+
+ // Is this Physique ?
+ if (ModifierPtr->ClassID() == Class_ID( PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B) )
+ {
+ // Yes -> Exit.
+ return ModifierPtr;
+ }
+ // Next modifier stack entry.
+ ModStackIndex++;
+ }
+ }
+ // Not found.
+ return NULL;
+}
+
+Modifier *FindBonesProModifier (INode *nodePtr)
+{
+ // Get object from node. Abort if no object.
+ Object *ObjectPtr = nodePtr->GetObjectRef();
+ if (!ObjectPtr) return NULL;
+
+ // Is derived object ?
+ if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
+ {
+ // Yes -> Cast.
+ IDerivedObject *DerivedObjectPtr = static_cast<IDerivedObject*>(ObjectPtr);
+
+ // Iterate over all entries of the modifier stack.
+ int ModStackIndex = 0;
+ while (ModStackIndex < DerivedObjectPtr->NumModifiers())
+ {
+ // Get current modifier.
+ Modifier *ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
+
+ // Is this Bones Pro OSM?
+ if (ModifierPtr->ClassID() == BP_CLASS_ID_OSM )
+ {
+ // Yes -> Exit.
+ return ModifierPtr;
+ }
+ // Is this Bones Pro WSM?
+ if (ModifierPtr->ClassID() == BP_CLASS_ID_WSM )
+ {
+ // Yes -> Exit.
+ return ModifierPtr;
+ }
+ // Next modifier stack entry.
+ ModStackIndex++;
+ }
+ }
+ // Not found.
+ return NULL;
+}
+
+// #define DEBUG_MESH_DUMP
+
+//=================================================================
+// Methods for DumpModelTEP
+//
+int DumpModelTEP::callback(INode *pnode)
+{
+ Object* pobj;
+ int fHasMat = TRUE;
+
+ // clear physique export parameters
+ m_mcExport = NULL;
+ m_phyExport = NULL;
+ m_phyMod = NULL;
+ m_bonesProMod = NULL;
+
+ ASSERT_MBOX(!(pnode)->IsRootNode(), "Encountered a root node!");
+
+ if (::FNodeMarkedToSkip(pnode))
+ return TREE_CONTINUE;
+
+ // Actually, if it's not selected, skip it!
+ //if (!pnode->Selected())
+ // return TRUE;
+
+ int iNode = ::GetIndexOfINode(pnode);
+ TSTR strNodeName(pnode->GetName());
+
+ // The Footsteps node apparently MUST have a dummy mesh attached! Ignore it explicitly.
+ if (FStrEq((char*)strNodeName, "Bip01 Footsteps"))
+ return TREE_CONTINUE;
+
+ // Helper nodes don't have meshes
+ pobj = pnode->GetObjectRef();
+ if (pobj->SuperClassID() == HELPER_CLASS_ID)
+ return TREE_CONTINUE;
+
+ // The model's root is a child of the real "scene root"
+ INode *pnodeParent = pnode->GetParentNode();
+ BOOL fNodeIsRoot = pnodeParent->IsRootNode( );
+
+ // Get node's material: should be a multi/sub (if it has a material at all)
+ Mtl *pmtlNode = pnode->GetMtl();
+ if (pmtlNode == NULL)
+ {
+ return TREE_CONTINUE;
+ fHasMat = FALSE;
+ }
+ else if (!(pmtlNode->ClassID() == Class_ID(MULTI_CLASS_ID, 0) && pmtlNode->IsMultiMtl()))
+ {
+ // sprintf(st_szDBG, "ERROR--Material on node %s isn't a Multi/Sub-Object", (char*)strNodeName);
+ // ASSERT_AND_ABORT(FALSE, st_szDBG);
+ // fHasMat = FALSE;
+ }
+
+ // Get Node's object, convert to a triangle-mesh object, so I can access the Faces
+ ObjectState os = pnode->EvalWorldState(m_tvToDump);
+ pobj = os.obj;
+ TriObject *ptriobj;
+ BOOL fConvertedToTriObject =
+ pobj->CanConvertToType(triObjectClassID) &&
+ (ptriobj = (TriObject*)pobj->ConvertToType(m_tvToDump, triObjectClassID)) != NULL;
+ if (!fConvertedToTriObject)
+ return TREE_CONTINUE;
+ Mesh *pmesh = &ptriobj->mesh;
+
+ // Shouldn't have gotten this far if it's a helper object
+ if (pobj->SuperClassID() == HELPER_CLASS_ID)
+ {
+ sprintf(st_szDBG, "ERROR--Helper node %s has an attached mesh, and it shouldn't.", (char*)strNodeName);
+ ASSERT_AND_ABORT(FALSE, st_szDBG);
+ }
+
+ // Ensure that the vertex normals are up-to-date
+ pmesh->buildNormals();
+
+ // We want the vertex coordinates in World-space, not object-space
+ Matrix3 mat3ObjectTM = pnode->GetObjectTM(m_tvToDump);
+
+
+ // initialize physique export parameters
+ m_phyMod = FindPhysiqueModifier(pnode);
+ if (m_phyMod)
+ {
+ // Physique Modifier exists for given Node
+ m_phyExport = (IPhysiqueExport *)m_phyMod->GetInterface(I_PHYINTERFACE);
+
+ if (m_phyExport)
+ {
+ // create a ModContext Export Interface for the specific node of the Physique Modifier
+ m_mcExport = (IPhyContextExport *)m_phyExport->GetContextInterface(pnode);
+
+ if (m_mcExport)
+ {
+ // convert all vertices to Rigid
+ m_mcExport->ConvertToRigid(TRUE);
+ }
+ }
+ }
+
+ // initialize bones pro export parameters
+ m_wa = NULL;
+ m_bonesProMod = FindBonesProModifier(pnode);
+ if (m_bonesProMod)
+ {
+ m_bonesProMod->SetProperty( BP_PROPID_GET_WEIGHTS, &m_wa );
+ }
+
+ // Dump the triangle face info
+ int cFaces = pmesh->getNumFaces();
+ for (int iFace = 0; iFace < cFaces; iFace++)
+ {
+ Face* pface = &pmesh->faces[iFace];
+ TVFace* ptvface = (pmesh->tvFace) ? &pmesh->tvFace[iFace] : NULL;
+ DWORD smGroupFace = pface->getSmGroup();
+
+ // Get face's 3 indexes into the Mesh's vertex array(s).
+ DWORD iVertex0 = pface->getVert(0);
+ DWORD iVertex1 = pface->getVert(1);
+ DWORD iVertex2 = pface->getVert(2);
+ ASSERT_AND_ABORT((int)iVertex0 < pmesh->getNumVerts(), "Bogus Vertex 0 index");
+ ASSERT_AND_ABORT((int)iVertex1 < pmesh->getNumVerts(), "Bogus Vertex 1 index");
+ ASSERT_AND_ABORT((int)iVertex2 < pmesh->getNumVerts(), "Bogus Vertex 2 index");
+
+ // Get the 3 Vertex's for this face
+ Point3 pt3Vertex0 = pmesh->getVert(iVertex0);
+ Point3 pt3Vertex1 = pmesh->getVert(iVertex1);
+ Point3 pt3Vertex2 = pmesh->getVert(iVertex2);
+
+ // Get the 3 RVertex's for this face
+ // NOTE: I'm using getRVertPtr instead of getRVert to work around a 3DSMax bug
+ RVertex *prvertex0 = pmesh->getRVertPtr(iVertex0);
+ RVertex *prvertex1 = pmesh->getRVertPtr(iVertex1);
+ RVertex *prvertex2 = pmesh->getRVertPtr(iVertex2);
+
+ // Find appropriate normals for each RVertex
+ // A vertex can be part of multiple faces, so the "smoothing group"
+ // is used to locate the normal for this face's use of the vertex.
+ Point3 pt3Vertex0Normal;
+ Point3 pt3Vertex1Normal;
+ Point3 pt3Vertex2Normal;
+ if (smGroupFace)
+ {
+ pt3Vertex0Normal = Pt3GetRVertexNormal(prvertex0, smGroupFace);
+ pt3Vertex1Normal = Pt3GetRVertexNormal(prvertex1, smGroupFace);
+ pt3Vertex2Normal = Pt3GetRVertexNormal(prvertex2, smGroupFace);
+ }
+ else
+ {
+ pt3Vertex0Normal = pmesh->getFaceNormal( iFace );
+ pt3Vertex1Normal = pmesh->getFaceNormal( iFace );
+ pt3Vertex2Normal = pmesh->getFaceNormal( iFace );
+ }
+ ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus orig normal 0" );
+ ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus orig normal 1" );
+ ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus orig normal 2" );
+
+ // Get Face's sub-material from node's material, to get the bitmap name.
+ // And no, there isn't a simpler way to get the bitmap name, you have to
+ // dig down through all these levels.
+ TCHAR szBitmapName[256] = "null.bmp";
+ if (fHasMat)
+ {
+ Texmap *ptexmap = NULL;
+ MtlID mtlidFace = pface->getMatID();
+ if (pmtlNode->IsMultiMtl())
+ {
+ if (mtlidFace >= pmtlNode->NumSubMtls())
+ {
+ sprintf(st_szDBG, "ERROR--Bogus sub-material index %d in node %s; highest valid index is %d",
+ mtlidFace, (char*)strNodeName, pmtlNode->NumSubMtls()-1);
+ // ASSERT_AND_ABORT(FALSE, st_szDBG);
+ mtlidFace = 0;
+ }
+ Mtl *pmtlFace = pmtlNode->GetSubMtl(mtlidFace);
+ ASSERT_AND_ABORT(pmtlFace != NULL, "NULL Sub-material returned");
+
+ /*
+ if ((pmtlFace->ClassID() == Class_ID(MULTI_CLASS_ID, 0) && pmtlFace->IsMultiMtl()))
+ {
+ // it's a sub-sub material. Gads.
+ pmtlFace = pmtlFace->GetSubMtl(mtlidFace);
+ ASSERT_AND_ABORT(pmtlFace != NULL, "NULL Sub-material returned");
+ }
+ */
+
+ if (!(pmtlFace->ClassID() == Class_ID(DMTL_CLASS_ID, 0)))
+ {
+
+ sprintf(st_szDBG,
+ "ERROR--Sub-material with index %d (used in node %s) isn't a 'default/standard' material [%x].",
+ mtlidFace, (char*)strNodeName, pmtlFace->ClassID());
+ ASSERT_AND_ABORT(FALSE, st_szDBG);
+ }
+ StdMat *pstdmtlFace = (StdMat*)pmtlFace;
+ ptexmap = pstdmtlFace->GetSubTexmap(ID_DI);
+ }
+ else
+ {
+ ptexmap = pmtlNode->GetActiveTexmap();
+ }
+
+ // ASSERT_AND_ABORT(ptexmap != NULL, "NULL diffuse texture")
+ if (ptexmap != NULL)
+ {
+ if (!(ptexmap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)))
+ {
+ sprintf(st_szDBG,
+ "ERROR--Sub-material with index %d (used in node %s) doesn't have a bitmap as its diffuse texture.",
+ mtlidFace, (char*)strNodeName);
+ ASSERT_AND_ABORT(FALSE, st_szDBG);
+ }
+ BitmapTex *pbmptex = (BitmapTex*)ptexmap;
+ strcpy(szBitmapName, pbmptex->GetMapName());
+ TSTR strPath, strFile;
+ SplitPathFile(TSTR(szBitmapName), &strPath, &strFile);
+ strcpy(szBitmapName,strFile);
+ }
+ }
+
+ UVVert UVvertex0( 0, 0, 0 );
+ UVVert UVvertex1( 1, 0, 0 );
+ UVVert UVvertex2( 0, 1, 0 );
+
+ // All faces must have textures assigned to them
+ if (ptvface && (pface->flags & HAS_TVERTS))
+ {
+ // Get TVface's 3 indexes into the Mesh's TVertex array(s).
+ DWORD iTVertex0 = ptvface->getTVert(0);
+ DWORD iTVertex1 = ptvface->getTVert(1);
+ DWORD iTVertex2 = ptvface->getTVert(2);
+ ASSERT_AND_ABORT((int)iTVertex0 < pmesh->getNumTVerts(), "Bogus TVertex 0 index");
+ ASSERT_AND_ABORT((int)iTVertex1 < pmesh->getNumTVerts(), "Bogus TVertex 1 index");
+ ASSERT_AND_ABORT((int)iTVertex2 < pmesh->getNumTVerts(), "Bogus TVertex 2 index");
+
+ // Get the 3 TVertex's for this TVFace
+ // NOTE: I'm using getRVertPtr instead of getRVert to work around a 3DSMax bug
+ UVvertex0 = pmesh->getTVert(iTVertex0);
+ UVvertex1 = pmesh->getTVert(iTVertex1);
+ UVvertex2 = pmesh->getTVert(iTVertex2);
+ }
+ else
+ {
+ //sprintf(st_szDBG, "ERROR--Node %s has a textureless face. All faces must have an applied texture.", (char*)strNodeName);
+ //ASSERT_AND_ABORT(FALSE, st_szDBG);
+ }
+
+ /*
+ const char *szExpectedExtension = ".bmp";
+ if (stricmp(szBitmapName+strlen(szBitmapName)-strlen(szExpectedExtension), szExpectedExtension) != 0)
+ {
+ sprintf(st_szDBG, "Node %s uses %s, which is not a %s file", (char*)strNodeName, szBitmapName, szExpectedExtension);
+ ASSERT_AND_ABORT(FALSE, st_szDBG);
+ }
+ */
+
+ // Determine owning bones for the vertices.
+ int iNodeV0, iNodeV1, iNodeV2;
+ // Simple 3dsMax model: the vertices are owned by the object, and hence the node
+ iNodeV0 = iNode;
+ iNodeV1 = iNode;
+ iNodeV2 = iNode;
+
+ // Rotate the face vertices out of object-space, and into world-space space
+ Point3 v0 = pt3Vertex0 * mat3ObjectTM;
+ Point3 v1 = pt3Vertex1 * mat3ObjectTM;
+ Point3 v2 = pt3Vertex2 * mat3ObjectTM;
+
+
+ Matrix3 mat3ObjectNTM = mat3ObjectTM;
+ mat3ObjectNTM.NoScale( );
+ ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus pre normal 0" );
+ pt3Vertex0Normal = VectorTransform(mat3ObjectNTM, pt3Vertex0Normal);
+ ASSERT_AND_ABORT( Length( pt3Vertex0Normal ) <= 1.1, "bogus post normal 0" );
+ ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus pre normal 1" );
+ pt3Vertex1Normal = VectorTransform(mat3ObjectNTM, pt3Vertex1Normal);
+ ASSERT_AND_ABORT( Length( pt3Vertex1Normal ) <= 1.1, "bogus post normal 1" );
+ ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus pre normal 2" );
+ pt3Vertex2Normal = VectorTransform(mat3ObjectNTM, pt3Vertex2Normal);
+ ASSERT_AND_ABORT( Length( pt3Vertex2Normal ) <= 1.1, "bogus post normal 2" );
+
+ // Finally dump the bitmap name and 3 lines of face info
+ fprintf(m_pfile, "%s\n", szBitmapName);
+ fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f",
+ iNodeV0, v0.x, v0.y, v0.z,
+ pt3Vertex0Normal.x, pt3Vertex0Normal.y, pt3Vertex0Normal.z,
+ UVvertex0.x, UVvertex0.y);
+ DumpWeights( iVertex0 );
+ fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f",
+ iNodeV1, v1.x, v1.y, v1.z,
+ pt3Vertex1Normal.x, pt3Vertex1Normal.y, pt3Vertex1Normal.z,
+ UVvertex1.x, UVvertex1.y);
+ DumpWeights( iVertex1 );
+ fprintf(m_pfile, "%3d %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f %8.4f",
+ iNodeV2, v2.x, v2.y, v2.z,
+ pt3Vertex2Normal.x, pt3Vertex2Normal.y, pt3Vertex2Normal.z,
+ UVvertex2.x, UVvertex2.y);
+ DumpWeights( iVertex2 );
+ }
+
+ cleanup( );
+ return TREE_CONTINUE;
+}
+
+
+#define MAX_BLEND_WEIGHTS 8
+
+static struct {
+ int iNode;
+ float flWeight;
+} aWeights[MAX_BLEND_WEIGHTS+1];
+
+int AddWeight( int iCount, int iNode, float flWeight )
+{
+ if (flWeight < 0.001)
+ return iCount;
+
+ for (int i = 0; i < iCount; i++)
+ {
+ if (aWeights[i].flWeight < flWeight)
+ {
+ for (int j = iCount; j > i; j--)
+ {
+ aWeights[j] = aWeights[j-1];
+ }
+ break;
+ }
+ }
+ aWeights[i].iNode = iNode;
+ aWeights[i].flWeight = flWeight;
+
+ iCount++;
+ if (iCount > MAX_BLEND_WEIGHTS)
+ iCount = MAX_BLEND_WEIGHTS;
+
+ return iCount;
+}
+
+
+void DumpModelTEP::DumpWeights(int iVertex)
+{
+ if (m_mcExport)
+ {
+ IPhyVertexExport *vtxExport = m_mcExport->GetVertexInterface(iVertex);
+
+ if (vtxExport)
+ {
+ if (vtxExport->GetVertexType() & BLENDED_TYPE)
+ {
+ IPhyBlendedRigidVertex *pBlend = ((IPhyBlendedRigidVertex *)vtxExport);
+ int iCount = 0;
+
+ for (int i = 0; i < pBlend->GetNumberNodes(); i++)
+ {
+ iCount = AddWeight( iCount, GetIndexOfINode( pBlend->GetNode( i ) ), pBlend->GetWeight( i ) );
+ }
+
+ fprintf(m_pfile, " %2d ", iCount );
+ for (i = 0; i < iCount; i++)
+ {
+ fprintf(m_pfile, " %2d %5.3f ", aWeights[i].iNode, aWeights[i].flWeight );
+ }
+ }
+ else
+ {
+ INode *Bone = ((IPhyRigidVertex *)vtxExport)->GetNode();
+
+ fprintf(m_pfile, " 1 %2d 1.000", GetIndexOfINode(Bone) );
+ }
+ m_mcExport->ReleaseVertexInterface(vtxExport);
+ }
+ }
+ else if (m_wa != NULL)
+ {
+ int iCount = 0;
+
+ for ( int iBone = 0; iBone < m_wa->nb; iBone++)
+ {
+ if (m_wa->w[iVertex * m_wa->nb + iBone] > 0.0)
+ {
+ BonesPro_Bone bone;
+ bone.t = BP_TIME_ATTACHED;
+ bone.index = iBone;
+ m_bonesProMod->SetProperty( BP_PROPID_GET_BONE_STAT, &bone );
+ if (bone.node != NULL)
+ {
+ iCount = AddWeight( iCount, GetIndexOfINode( bone.node ), m_wa->w[iVertex * m_wa->nb + iBone] );
+ }
+ }
+ }
+
+ fprintf(m_pfile, " %2d ", iCount );
+ for (int i = 0; i < iCount; i++)
+ {
+ fprintf(m_pfile, " %2d %5.3f ", aWeights[i].iNode, aWeights[i].flWeight );
+ }
+ }
+
+ fprintf(m_pfile, "\n" );
+ fflush( m_pfile );
+}
+
+void DumpModelTEP::cleanup(void)
+{
+ if (m_phyMod && m_phyExport)
+ {
+ if (m_mcExport)
+ {
+ m_phyExport->ReleaseContextInterface(m_mcExport);
+ m_mcExport = NULL;
+ }
+ m_phyMod->ReleaseInterface(I_PHYINTERFACE, m_phyExport);
+ m_phyExport = NULL;
+ m_phyMod = NULL;
+ }
+}
+
+
+Point3 DumpModelTEP::Pt3GetRVertexNormal(RVertex *prvertex, DWORD smGroupFace)
+{
+ // Lookup the appropriate vertex normal, based on smoothing group.
+ int cNormals = prvertex->rFlags & NORCT_MASK;
+
+ ASSERT_MBOX((cNormals == 1 && prvertex->ern == NULL) ||
+ (cNormals > 1 && prvertex->ern != NULL), "BOGUS RVERTEX");
+
+ if (cNormals == 1)
+ return prvertex->rn.getNormal();
+ else
+ {
+ for (int irn = 0; irn < cNormals; irn++)
+ if (prvertex->ern[irn].getSmGroup() & smGroupFace)
+ break;
+
+ if (irn >= cNormals)
+ {
+ irn = 0;
+ // ASSERT_MBOX(irn < cNormals, "unknown smoothing group\n");
+ }
+ return prvertex->ern[irn].getNormal();
+ }
+}
+
+
+
+
+
+//===========================================================
+// Dialog proc for export options
+//
+static BOOL CALLBACK ExportOptionsDlgProc(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ static SmdExportClass *pexp;
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ pexp = (SmdExportClass*) lParam;
+ CheckRadioButton(hDlg, IDC_CHECK_SKELETAL, IDC_CHECK_REFFRAME, IDC_CHECK_SKELETAL);
+ SetFocus(GetDlgItem(hDlg,IDOK));
+ return FALSE;
+ case WM_DESTROY:
+ return FALSE;
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ pexp->m_fReferenceFrame = IsDlgButtonChecked(hDlg, IDC_CHECK_REFFRAME);
+ EndDialog(hDlg, 1); // 1 indicates "ok to export"
+ return TRUE;
+ case IDCANCEL: // 0 indicates "cancel export"
+ EndDialog(hDlg, 0);
+ return TRUE;
+ case IDC_CHECK_SKELETAL:
+ case IDC_CHECK_REFFRAME:
+ CheckRadioButton(hDlg, IDC_CHECK_SKELETAL, IDC_CHECK_REFFRAME, LOWORD(wParam));
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+
+//========================================================================
+// Utility functions for getting/setting the personal "node index" property.
+// NOTE: I'm storing a string-property because I hit a 3DSMax bug in v1.2 when I
+// NOTE: tried using an integer property.
+// FURTHER NOTE: those properties seem to change randomly sometimes, so I'm
+// implementing my own.
+
+typedef struct
+{
+ char szNodeName[SmdExportClass::MAX_NAME_CHARS];
+ int iNode;
+} NAMEMAP;
+const int MAX_NAMEMAP = 512;
+static NAMEMAP g_rgnm[MAX_NAMEMAP];
+
+int GetIndexOfINode(INode *pnode, BOOL fAssertPropExists)
+{
+ TSTR strNodeName(pnode->GetName());
+ for (int inm = 0; inm < g_inmMac; inm++)
+ {
+ if (FStrEq(g_rgnm[inm].szNodeName, (char*)strNodeName))
+ {
+ return g_rgnm[inm].iNode;
+ }
+ }
+
+ if (fAssertPropExists)
+ ASSERT_MBOX(FALSE, "No NODEINDEXSTR property");
+ return -7777;
+}
+
+
+void SetIndexOfINode(INode *pnode, int inode)
+{
+ TSTR strNodeName(pnode->GetName());
+ NAMEMAP *pnm;
+ for (int inm = 0; inm < g_inmMac; inm++)
+ if (FStrEq(g_rgnm[inm].szNodeName, (char*)strNodeName))
+ break;
+ if (inm < g_inmMac)
+ pnm = &g_rgnm[inm];
+ else
+ {
+ ASSERT_MBOX(g_inmMac < MAX_NAMEMAP, "NAMEMAP is full");
+ pnm = &g_rgnm[g_inmMac++];
+ strcpy(pnm->szNodeName, (char*)strNodeName);
+ }
+ pnm->iNode = inode;
+}
+
+
+//=============================================================
+// Returns TRUE if a node should be ignored during tree traversal.
+//
+BOOL FUndesirableNode(INode *pnode)
+{
+ // Get Node's underlying object, and object class name
+ Object *pobj = pnode->GetObjectRef();
+
+ // Don't care about lights, dummies, and cameras
+ if (pobj->SuperClassID() == CAMERA_CLASS_ID)
+ return TRUE;
+ if (pobj->SuperClassID() == LIGHT_CLASS_ID)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+//=============================================================
+// Returns TRUE if a node has been marked as skippable
+//
+BOOL FNodeMarkedToSkip(INode *pnode)
+{
+ return (::GetIndexOfINode(pnode) == SmdExportClass::UNDESIRABLE_NODE_MARKER);
+}
+
+
+//=============================================================
+// Reduces a rotation to within the -2PI..2PI range.
+//
+static float FlReduceRotation(float fl)
+{
+ while (fl >= TWOPI)
+ fl -= TWOPI;
+ while (fl <= -TWOPI)
+ fl += TWOPI;
+ return fl;
+}
diff --git a/mp/src/utils/smdlexp/smdlexp.def b/mp/src/utils/smdlexp/smdlexp.def new file mode 100644 index 00000000..d06167f3 --- /dev/null +++ b/mp/src/utils/smdlexp/smdlexp.def @@ -0,0 +1,8 @@ +LIBRARY smdlexp
+EXPORTS
+ LibDescription @1
+ LibNumberClasses @2
+ LibClassDesc @3
+ LibVersion @4
+SECTIONS
+ .data READ WRITE
diff --git a/mp/src/utils/smdlexp/smdlexp.mak b/mp/src/utils/smdlexp/smdlexp.mak new file mode 100644 index 00000000..f0f7ecc0 --- /dev/null +++ b/mp/src/utils/smdlexp/smdlexp.mak @@ -0,0 +1,325 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+!IF "$(CFG)" == ""
+CFG=smdlexp - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to smdlexp - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "smdlexp - Win32 Release" && "$(CFG)" !=\
+ "smdlexp - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "smdlexp.mak" CFG="smdlexp - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "smdlexp - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "smdlexp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "smdlexp - Win32 Debug"
+RSC=rc.exe
+MTL=mktyplib.exe
+CPP=cl.exe
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+OUTDIR=.\Release
+INTDIR=.\Release
+
+ALL : "..\..\..\3DSMAX\STDPLUGS\SMDLEXP.DLE"
+
+CLEAN :
+ -@erase "$(INTDIR)\smdlexp.obj"
+ -@erase "$(INTDIR)\smdlexp.res"
+ -@erase "$(OUTDIR)\SMDLEXP.exp"
+ -@erase "$(OUTDIR)\SMDLEXP.lib"
+ -@erase "..\..\..\3DSMAX\STDPLUGS\SMDLEXP.DLE"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "\3DSMAX2.5\MAXSDK\INCLUDE" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "\3DSMAX2.5\MAXSDK\INCLUDE" /D "WIN32" /D\
+ "NDEBUG" /D "_WINDOWS" /Fp"$(INTDIR)/smdlexp.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/smdlexp.res" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/smdlexp.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib COMCTL32.LIB /nologo /subsystem:windows /dll /machine:I386 /out:"\3DSMAX\STDPLUGS\SMDLEXP.DLE"
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib COMCTL32.LIB /nologo\
+ /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)/SMDLEXP.pdb"\
+ /machine:I386 /def:".\smdlexp.def" /out:"\3DSMAX\STDPLUGS\SMDLEXP.DLE"\
+ /implib:"$(OUTDIR)/SMDLEXP.lib"
+DEF_FILE= \
+ ".\smdlexp.def"
+LINK32_OBJS= \
+ "$(INTDIR)\smdlexp.obj" \
+ "$(INTDIR)\smdlexp.res" \
+ "..\..\..\quiver\src\utils\3dsmax\CORE.LIB" \
+ "..\..\..\quiver\src\utils\3dsmax\GEOM.LIB" \
+ "..\..\..\quiver\src\utils\3dsmax\MESH.LIB" \
+ "..\..\..\quiver\src\utils\3dsmax\UTIL.LIB"
+
+"..\..\..\3DSMAX\STDPLUGS\SMDLEXP.DLE" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+OUTDIR=.\Debug
+INTDIR=.\Debug
+
+ALL : "..\..\..\3DSMAX\STDPLUGS\SMDLEXP.DLE" "$(OUTDIR)\smdlexp.bsc"
+
+CLEAN :
+ -@erase "$(INTDIR)\smdlexp.obj"
+ -@erase "$(INTDIR)\smdlexp.res"
+ -@erase "$(INTDIR)\smdlexp.sbr"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\smdlexp.bsc"
+ -@erase "$(OUTDIR)\SMDLEXP.exp"
+ -@erase "$(OUTDIR)\SMDLEXP.lib"
+ -@erase "$(OUTDIR)\SMDLEXP.pdb"
+ -@erase "..\..\..\3DSMAX\STDPLUGS\SMDLEXP.DLE"
+ -@erase "..\..\..\3DSMAX\STDPLUGS\SMDLEXP.ILK"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "\3DSMAX2.5\MAXSDK\INCLUDE" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /c
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "\3DSMAX2.5\MAXSDK\INCLUDE" /D\
+ "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"$(INTDIR)/" /Fp"$(INTDIR)/smdlexp.pch"\
+ /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.\Debug/
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)/smdlexp.res" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/smdlexp.bsc"
+BSC32_SBRS= \
+ "$(INTDIR)\smdlexp.sbr"
+
+"$(OUTDIR)\smdlexp.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib COMCTL32.LIB /nologo /subsystem:windows /dll /debug /machine:I386 /out:"\3DSMAX\STDPLUGS\SMDLEXP.DLE"
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib COMCTL32.LIB /nologo\
+ /subsystem:windows /dll /incremental:yes /pdb:"$(OUTDIR)/SMDLEXP.pdb" /debug\
+ /machine:I386 /def:".\smdlexp.def" /out:"\3DSMAX\STDPLUGS\SMDLEXP.DLE"\
+ /implib:"$(OUTDIR)/SMDLEXP.lib"
+DEF_FILE= \
+ ".\smdlexp.def"
+LINK32_OBJS= \
+ "$(INTDIR)\smdlexp.obj" \
+ "$(INTDIR)\smdlexp.res" \
+ "..\..\..\quiver\src\utils\3dsmax\CORE.LIB" \
+ "..\..\..\quiver\src\utils\3dsmax\GEOM.LIB" \
+ "..\..\..\quiver\src\utils\3dsmax\MESH.LIB" \
+ "..\..\..\quiver\src\utils\3dsmax\UTIL.LIB"
+
+"..\..\..\3DSMAX\STDPLUGS\SMDLEXP.DLE" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "smdlexp - Win32 Release"
+# Name "smdlexp - Win32 Debug"
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=.\smdlexp.cpp
+DEP_CPP_SMDLE=\
+ ".\smedefs.h"\
+
+NODEP_CPP_SMDLE=\
+ ".\ANIMTBL.H"\
+ ".\DECOMP.H"\
+ ".\istdplug.h"\
+ ".\MAX.H"\
+ ".\STDMAT.H"\
+
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+
+"$(INTDIR)\smdlexp.obj" : $(SOURCE) $(DEP_CPP_SMDLE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+
+"$(INTDIR)\smdlexp.obj" : $(SOURCE) $(DEP_CPP_SMDLE) "$(INTDIR)"
+
+"$(INTDIR)\smdlexp.sbr" : $(SOURCE) $(DEP_CPP_SMDLE) "$(INTDIR)"
+
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\smdlexp.def
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=.\smdlexp.rc
+
+"$(INTDIR)\smdlexp.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quiver\src\utils\3dsmax\UTIL.LIB
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quiver\src\utils\3dsmax\GEOM.LIB
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quiver\src\utils\3dsmax\MESH.LIB
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+################################################################################
+# Begin Source File
+
+SOURCE=\quiver\src\utils\3dsmax\CORE.LIB
+
+!IF "$(CFG)" == "smdlexp - Win32 Release"
+
+!ELSEIF "$(CFG)" == "smdlexp - Win32 Debug"
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
+################################################################################
diff --git a/mp/src/utils/smdlexp/smdlexp.rc b/mp/src/utils/smdlexp/smdlexp.rc new file mode 100644 index 00000000..4a20ba4f --- /dev/null +++ b/mp/src/utils/smdlexp/smdlexp.rc @@ -0,0 +1,147 @@ +//Microsoft Developer Studio generated resource script.
+//
+#include "smexprc.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_EXPORTOPTIONS DIALOG DISCARDABLE 0, 0, 186, 42
+STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "SMD Exporter"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,129,7,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,129,22,50,14
+ CONTROL "Skeletal Animation",IDC_CHECK_SKELETAL,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,15,10,74,10
+ CONTROL "Reference Frame",IDC_CHECK_REFFRAME,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,15,24,71,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_EXPORTOPTIONS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 36
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "smexprc.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,0,0,1
+ PRODUCTVERSION 2,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "\0"
+ VALUE "CompanyName", "Valve LLC\0"
+ VALUE "FileDescription", "SMD file exporter (3D Studio Max plugin)\0"
+ VALUE "FileVersion", "2, 0, 0, 1\0"
+ VALUE "InternalName", "SMDLEXP\0"
+ VALUE "LegalCopyright", "Copyright � 1998, Valve LLC\0"
+ VALUE "LegalTrademarks", "The following are registered trademarks of Autodesk, Inc.: 3D Studio MAX. The following are trademarks of Autodesk, Inc.: Kinetix, Kinetix(logo), BIPED, Physique, Character Studio, MAX DWG, DWG Unplugged, Heidi, FLI, FLC, DXF.\0"
+ VALUE "OriginalFilename", "SMDLEXP.DLE\0"
+ VALUE "PrivateBuild", "\0"
+ VALUE "ProductName", "Valve LLC SMDLEXP\0"
+ VALUE "ProductVersion", "2, 0, 0, 1\0"
+ VALUE "SpecialBuild", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/mp/src/utils/smdlexp/smedefs.h b/mp/src/utils/smdlexp/smedefs.h new file mode 100644 index 00000000..7211e1e1 --- /dev/null +++ b/mp/src/utils/smdlexp/smedefs.h @@ -0,0 +1,178 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+//===================================================================
+// Useful macros
+//
+#define CONSTRUCTOR
+#define DESTRUCTOR
+
+#define EXPORT_THIS __declspec(dllexport)
+
+#define DEFAULT_EXT _T("smd")
+
+#define FStrEq(sz1, sz2) (strcmp((sz1), (sz2)) == 0)
+
+
+//===================================================================
+// Class that implements the scene-export.
+//
+class SmdExportClass : public SceneExport
+{
+ friend BOOL CALLBACK ExportOptionsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+ friend class DumpModelTEP;
+ friend class DumpDeformsTEP;
+
+public:
+ CONSTRUCTOR SmdExportClass (void);
+ DESTRUCTOR ~SmdExportClass (void);
+
+ // Required by classes derived from SceneExport
+ virtual int ExtCount (void) { return 1; }
+ virtual const TCHAR* Ext (int i) { return DEFAULT_EXT; }
+ virtual const TCHAR* LongDesc (void) { return _T("Valve Skeletal Model Exporter for 3D Studio Max"); }
+ virtual const TCHAR* ShortDesc (void) { return _T("Valve SMD"); }
+ virtual const TCHAR* AuthorName (void) { return _T("Valve, LLC"); }
+ virtual const TCHAR* CopyrightMessage(void) { return _T("Copyright (c) 1998, Valve LLC"); }
+ virtual const TCHAR* OtherMessage1 (void) { return _T(""); }
+ virtual const TCHAR* OtherMessage2 (void) { return _T(""); }
+ virtual unsigned int Version (void) { return 201; }
+ virtual void ShowAbout (HWND hWnd) { return; }
+ // virtual int DoExport (const TCHAR *name, ExpInterface *ei, Interface *i);
+ virtual int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE,DWORD options=0); // Export file
+
+ // Integer constants for this class
+ enum
+ {
+ MAX_NAME_CHARS = 70,
+ UNDESIRABLE_NODE_MARKER = -7777
+ };
+
+ // For keeping info about each (non-ignored) 3dsMax node in the tree
+ typedef struct
+ {
+ char szNodeName[MAX_NAME_CHARS]; // usefull for lookups
+ Matrix3 mat3NodeTM; // node's transformation matrix (at time zero)
+ Matrix3 mat3ObjectTM; // object-offset transformation matrix (at time zero)
+ int imaxnodeParent; // cached index of parent node
+ float xRotFirstFrame; // 1st frame's X rotation
+ float yRotFirstFrame; // 1st frame's Y rotation
+ float zRotFirstFrame; // 1st frame's Z rotation
+ bool isMirrored;
+ } MaxNode;
+ MaxNode *m_rgmaxnode; // array of nodes
+ long m_imaxnodeMac; // # of nodes
+
+ // Animation metrics (gleaned from 3dsMax and cached for convenience)
+ Interval m_intervalOfAnimation;
+ TimeValue m_tvStart;
+ TimeValue m_tvEnd;
+ int m_tpf; // ticks-per-frame
+
+private:
+ BOOL CollectNodes (ExpInterface *expiface);
+ BOOL DumpBones (FILE *pFile, ExpInterface *pexpiface);
+ BOOL DumpRotations (FILE *pFile, ExpInterface *pexpiface);
+ BOOL DumpModel (FILE *pFile, ExpInterface *pexpiface);
+ BOOL DumpDeforms (FILE *pFile, ExpInterface *pexpiface);
+
+ // Is this MAX file just the reference frame, or an animation?
+ // If TRUE, the "bones" and "mesh" files will be created.
+ // If FALSE, the "rots" file will be created.
+ BOOL m_fReferenceFrame;
+};
+
+
+//===================================================================
+// Basically just a ClassFactory for communicating with 3DSMAX.
+//
+class SmdExportClassDesc : public ClassDesc
+{
+public:
+ int IsPublic (void) { return TRUE; }
+ void * Create (BOOL loading=FALSE) { return new SmdExportClass; }
+ const TCHAR * ClassName (void) { return _T("SmdExport"); }
+ SClass_ID SuperClassID (void) { return SCENE_EXPORT_CLASS_ID; }
+ Class_ID ClassID (void) { return Class_ID(0x774a43fd, 0x794d2210); }
+ const TCHAR * Category (void) { return _T(""); }
+};
+
+
+//===================================================================
+// Tree Enumeration Callback
+// Just counts the nodes in the node tree
+//
+class CountNodesTEP : public ITreeEnumProc
+{
+public:
+ virtual int callback(INode *node);
+ int m_cNodes; // running count of nodes
+};
+
+
+//===================================================================
+// Tree Enumeration Callback
+// Collects the nodes in the tree into the global array
+//
+class CollectNodesTEP : public ITreeEnumProc
+{
+public:
+ virtual int callback(INode *node);
+ SmdExportClass *m_phec;
+};
+
+
+//===================================================================
+// Tree Enumeration Callback
+// Dumps the bone offsets to a file.
+//
+class DumpNodesTEP : public ITreeEnumProc
+{
+public:
+ virtual int callback(INode *node);
+ FILE *m_pfile; // write to this file
+ SmdExportClass *m_phec;
+};
+
+
+//===================================================================
+// Tree Enumeration Callback
+// Dumps the per-frame bone rotations to a file.
+//
+class DumpFrameRotationsTEP : public ITreeEnumProc
+{
+public:
+ virtual int callback(INode *node);
+ void cleanup(void);
+ FILE *m_pfile; // write to this file
+ TimeValue m_tvToDump; // dump snapshot at this frame time
+ SmdExportClass *m_phec;
+};
+
+//===================================================================
+// Tree Enumeration Callback
+// Dumps the triangle meshes to a file.
+//
+class DumpModelTEP : public ITreeEnumProc
+{
+public:
+ virtual int callback(INode *node);
+ void cleanup(void);
+ FILE *m_pfile; // write to this file
+ TimeValue m_tvToDump; // dump snapshot at this frame time
+ SmdExportClass *m_phec;
+ IPhyContextExport *m_mcExport;
+ IPhysiqueExport *m_phyExport;
+ Modifier *m_phyMod;
+ Modifier *m_bonesProMod;
+ BonesPro_WeightArray *m_wa;
+private:
+ Point3 Pt3GetRVertexNormal(RVertex *prvertex, DWORD smGroupFace);
+ void DumpWeights( int iVertex );
+};
+
diff --git a/mp/src/utils/smdlexp/smexprc.h b/mp/src/utils/smdlexp/smexprc.h new file mode 100644 index 00000000..d783fc67 --- /dev/null +++ b/mp/src/utils/smdlexp/smexprc.h @@ -0,0 +1,28 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by smdlexp.rc
+//
+#define IDD_SMDLEXP_UI 101
+#define IDD_EXPORTOPTIONS 101
+#define IDC_CHECK_SKELETAL 1000
+#define IDC_CHECK_DEFORM 1001
+#define IDC_CHECK_REFFRAME 1002
+#define IDC_CHECK_PHYSIQUE 1003
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1006
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/mp/src/utils/tgadiff/tgadiff-2010.vcxproj b/mp/src/utils/tgadiff/tgadiff-2010.vcxproj new file mode 100644 index 00000000..85a5c581 --- /dev/null +++ b/mp/src/utils/tgadiff/tgadiff-2010.vcxproj @@ -0,0 +1,243 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Tgadiff</ProjectName>
+ <ProjectGuid>{C6A1B4E3-DFD8-CD7B-5CBF-D3267A96FF21}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>tgadiff</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>tgadiff</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 tgadiff.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\tgadiff;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\tgadiff.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/tgadiff.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 tgadiff.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\tgadiff;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\tgadiff.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/tgadiff.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib" />
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="tgadiff.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/tgadiff/tgadiff-2010.vcxproj.filters b/mp/src/utils/tgadiff/tgadiff-2010.vcxproj.filters new file mode 100644 index 00000000..f55777eb --- /dev/null +++ b/mp/src/utils/tgadiff/tgadiff-2010.vcxproj.filters @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="tgadiff.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/tgadiff/tgadiff.cpp b/mp/src/utils/tgadiff/tgadiff.cpp new file mode 100644 index 00000000..6dbc3e3a --- /dev/null +++ b/mp/src/utils/tgadiff/tgadiff.cpp @@ -0,0 +1,184 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#include <stdlib.h>
+#include <stdio.h>
+#include <direct.h>
+#include "bitmap/tgaloader.h"
+#include "bitmap/tgawriter.h"
+#include "tier1/utlbuffer.h"
+#include "tier2/tier2.h"
+#include "mathlib/mathlib.h"
+#include "filesystem.h"
+
+void Usage( void )
+{
+ printf( "Usage: tgadiff src1.tga src2.tga diff.tga\n" );
+ exit( -1 );
+}
+
+int main( int argc, char **argv )
+{
+ if( argc != 4 )
+ {
+ Usage();
+ }
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
+ InitDefaultFileSystem();
+
+ char pCurrentDirectory[MAX_PATH];
+ if ( _getcwd( pCurrentDirectory, sizeof(pCurrentDirectory) ) == NULL )
+ {
+ fprintf( stderr, "Unable to get the current directory\n" );
+ return -1;
+ }
+ Q_FixSlashes( pCurrentDirectory );
+ Q_StripTrailingSlash( pCurrentDirectory );
+
+ char pBuf[3][MAX_PATH];
+ const char *pFileName[3];
+ for ( int i = 0; i < 3; ++i )
+ {
+ if ( !Q_IsAbsolutePath( argv[i+1] ) )
+ {
+ Q_snprintf( pBuf[i], sizeof(pBuf[i]), "%s\\%s", pCurrentDirectory, argv[i+1] );
+ pFileName[i] = pBuf[i];
+ }
+ else
+ {
+ pFileName[i] = argv[i+1];
+ }
+ }
+
+ int width1, height1;
+ ImageFormat imageFormat1;
+ float gamma1;
+
+ CUtlBuffer buf1;
+ if ( !g_pFullFileSystem->ReadFile( pFileName[0], NULL, buf1 ) )
+ {
+ fprintf( stderr, "%s not found\n", pFileName[0] );
+ return -1;
+ }
+
+ if( !TGALoader::GetInfo( buf1, &width1, &height1, &imageFormat1, &gamma1 ) )
+ {
+ printf( "error loading %s\n", pFileName[0] );
+ exit( -1 );
+ }
+
+ int width2, height2;
+ ImageFormat imageFormat2;
+ float gamma2;
+
+ CUtlBuffer buf2;
+ if ( !g_pFullFileSystem->ReadFile( pFileName[1], NULL, buf2 ) )
+ {
+ fprintf( stderr, "%s not found\n", pFileName[1] );
+ return -1;
+ }
+
+ if( !TGALoader::GetInfo( buf2, &width2, &height2, &imageFormat2, &gamma2 ) )
+ {
+ printf( "error loading %s\n", pFileName[1] );
+ exit( -1 );
+ }
+
+ if( width1 != width2 || height1 != height2 )
+ {
+ printf( "image dimensions different (%dx%d!=%dx%d): can't do diff for %s\n",
+ width1, height1, width2, height2, pFileName[2] );
+ exit( -1 );
+ }
+#if 0
+ // have to allow for different formats for now due to *.txt file screwup.
+ if( imageFormat1 != imageFormat2 )
+ {
+ printf( "image format different (%s!=%s). . can't do diff for %s\n",
+ ImageLoader::GetName( imageFormat1 ), ImageLoader::GetName( imageFormat2 ), pFileName[2] );
+ exit( -1 );
+ }
+#endif
+ if( gamma1 != gamma2 )
+ {
+ printf( "image gamma different (%f!=%f). . can't do diff for %s\n", gamma1, gamma2, pFileName[2] );
+ exit( -1 );
+ }
+
+ unsigned char *pImage1Tmp = new unsigned char[ImageLoader::GetMemRequired( width1, height1, 1, imageFormat1, false )];
+ unsigned char *pImage2Tmp = new unsigned char[ImageLoader::GetMemRequired( width2, height2, 1, imageFormat2, false )];
+
+ buf1.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ if( !TGALoader::Load( pImage1Tmp, buf1, width1, height1, imageFormat1, 2.2f, false ) )
+ {
+ printf( "error loading %s\n", pFileName[0] );
+ exit( -1 );
+ }
+
+ buf2.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ if( !TGALoader::Load( pImage2Tmp, buf2, width2, height2, imageFormat2, 2.2f, false ) )
+ {
+ printf( "error loading %s\n", pFileName[1] );
+ exit( -1 );
+ }
+
+ unsigned char *pImage1 = new unsigned char[ImageLoader::GetMemRequired( width1, height1, 1, IMAGE_FORMAT_ABGR8888, false )];
+ unsigned char *pImage2 = new unsigned char[ImageLoader::GetMemRequired( width2, height2, 1, IMAGE_FORMAT_ABGR8888, false )];
+ unsigned char *pDiff = new unsigned char[ImageLoader::GetMemRequired( width2, height2, 1, IMAGE_FORMAT_ABGR8888, false )];
+ ImageLoader::ConvertImageFormat( pImage1Tmp, imageFormat1, pImage1, IMAGE_FORMAT_ABGR8888, width1, height1, 0, 0 );
+ ImageLoader::ConvertImageFormat( pImage2Tmp, imageFormat2, pImage2, IMAGE_FORMAT_ABGR8888, width2, height2, 0, 0 );
+
+ int sizeInBytes = ImageLoader::SizeInBytes( IMAGE_FORMAT_ABGR8888 );
+ bool isDifferent = false;
+ for( int i = 0; i < width1 * height1 * sizeInBytes; i++ )
+ {
+ int d;
+ d = pImage2[i] - pImage1[i];
+ pDiff[i] = d > 0 ? d : -d;
+ if( d != 0 )
+ {
+ isDifferent = true;
+ }
+ }
+
+ if( !isDifferent )
+ {
+ printf( "Files are the same %s %s : not generating %s\n", pFileName[0], pFileName[1], pFileName[2] );
+ exit( -1 );
+ }
+ else
+ {
+ printf( "Generating diff: %s!\n", pFileName[2] );
+ }
+
+ ImageFormat dstImageFormat;
+ // get rid of this until we get the formats matching
+// if( sizeInBytes == 3 )
+// {
+// dstImageFormat = IMAGE_FORMAT_RGB888;
+// }
+// else
+ {
+ dstImageFormat = IMAGE_FORMAT_RGBA8888;
+ }
+
+ CUtlBuffer outBuffer;
+ if ( !TGAWriter::WriteToBuffer( pDiff, outBuffer, width1, height1, dstImageFormat, dstImageFormat ) )
+ {
+ printf( "error writing %s to buffer\n", pFileName[2] );
+ exit( -1 );
+ }
+
+ if ( !g_pFullFileSystem->WriteFile( pFileName[2], NULL, outBuffer ) )
+ {
+ fprintf( stderr, "unable to write %s\n", pFileName[2] );
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/mp/src/utils/vbsp/boundbox.cpp b/mp/src/utils/vbsp/boundbox.cpp new file mode 100644 index 00000000..d0366cfc --- /dev/null +++ b/mp/src/utils/vbsp/boundbox.cpp @@ -0,0 +1,285 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "BoundBox.h"
+//#include "hammer_mathlib.h"
+//#include "MapDefs.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+float rint(float f)
+{
+ if (f > 0.0f) {
+ return (float) floor(f + 0.5f);
+ } else if (f < 0.0f) {
+ return (float) ceil(f - 0.5f);
+ } else
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+BoundBox::BoundBox(void)
+{
+ ResetBounds();
+}
+
+BoundBox::BoundBox(const Vector &mins, const Vector &maxs)
+{
+ bmins = mins;
+ bmaxs = maxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the box to an uninitialized state, so that calls to UpdateBounds
+// will properly set the mins and maxs.
+//-----------------------------------------------------------------------------
+void BoundBox::ResetBounds(void)
+{
+ bmins[0] = bmins[1] = bmins[2] = COORD_NOTINIT;
+ bmaxs[0] = bmaxs[1] = bmaxs[2] = -COORD_NOTINIT;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pt -
+//-----------------------------------------------------------------------------
+void BoundBox::UpdateBounds(const Vector& pt)
+{
+ if(pt[0] < bmins[0])
+ bmins[0] = pt[0];
+ if(pt[1] < bmins[1])
+ bmins[1] = pt[1];
+ if(pt[2] < bmins[2])
+ bmins[2] = pt[2];
+
+ if(pt[0] > bmaxs[0])
+ bmaxs[0] = pt[0];
+ if(pt[1] > bmaxs[1])
+ bmaxs[1] = pt[1];
+ if(pt[2] > bmaxs[2])
+ bmaxs[2] = pt[2];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bmins -
+// bmaxs -
+//-----------------------------------------------------------------------------
+void BoundBox::UpdateBounds(const Vector& mins, const Vector& maxs)
+{
+ if(mins[0] < bmins[0])
+ bmins[0] = mins[0];
+ if(mins[1] < bmins[1])
+ bmins[1] = mins[1];
+ if(mins[2] < bmins[2])
+ bmins[2] = mins[2];
+
+ if(maxs[0] > bmaxs[0])
+ bmaxs[0] = maxs[0];
+ if(maxs[1] > bmaxs[1])
+ bmaxs[1] = maxs[1];
+ if(maxs[2] > bmaxs[2])
+ bmaxs[2] = maxs[2];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pBox -
+//-----------------------------------------------------------------------------
+void BoundBox::UpdateBounds(const BoundBox *pBox)
+{
+ UpdateBounds(pBox->bmins, pBox->bmaxs);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : ptdest -
+//-----------------------------------------------------------------------------
+void BoundBox::GetBoundsCenter(Vector& ptdest)
+{
+ ptdest = (bmins + bmaxs)/2.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pt -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool BoundBox::ContainsPoint(const Vector& pt) const
+{
+ for (int i = 0; i < 3; i++)
+ {
+ if (pt[i] < bmins[i] || pt[i] > bmaxs[i])
+ {
+ return(false);
+ }
+ }
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pfMins -
+// pfMaxs -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool BoundBox::IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const
+{
+ if ((bmins[0] >= pfMaxs[0]) || (bmaxs[0] <= pfMins[0]))
+ {
+ return(false);
+
+ }
+ if ((bmins[1] >= pfMaxs[1]) || (bmaxs[1] <= pfMins[1]))
+ {
+ return(false);
+ }
+
+ if ((bmins[2] >= pfMaxs[2]) || (bmaxs[2] <= pfMins[2]))
+ {
+ return(false);
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pfMins -
+// pfMaxs -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool BoundBox::IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const
+{
+ if ((bmins[0] < pfMins[0]) || (bmaxs[0] > pfMaxs[0]))
+ {
+ return(false);
+ }
+
+ if ((bmins[1] < pfMins[1]) || (bmaxs[1] > pfMaxs[1]))
+ {
+ return(false);
+ }
+
+ if ((bmins[2] < pfMins[2]) || (bmaxs[2] > pfMaxs[2]))
+ {
+ return(false);
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether this bounding box is valid, ie maxs >= mins.
+//-----------------------------------------------------------------------------
+bool BoundBox::IsValidBox(void) const
+{
+ for (int i = 0; i < 3; i++)
+ {
+ if (bmins[i] > bmaxs[i])
+ {
+ return(false);
+ }
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : size -
+//-----------------------------------------------------------------------------
+void BoundBox::GetBoundsSize(Vector& size)
+{
+ size[0] = bmaxs[0] - bmins[0];
+ size[1] = bmaxs[1] - bmins[1];
+ size[2] = bmaxs[2] - bmins[2];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iValue -
+// iGridSize -
+// Output :
+//-----------------------------------------------------------------------------
+static int Snap(/*int*/ float iValue, int iGridSize)
+{
+ return (int)(rint(iValue/iGridSize) * iGridSize);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iGridSize -
+//-----------------------------------------------------------------------------
+void BoundBox::SnapToGrid(int iGridSize)
+{
+ // does not alter the size of the box .. snaps its minimal coordinates
+ // to the grid size specified in iGridSize
+ Vector size;
+ GetBoundsSize(size);
+
+ for(int i = 0; i < 3; i++)
+ {
+ bmins[i] = (float)Snap(/* YWB (int)*/bmins[i], iGridSize);
+ bmaxs[i] = bmins[i] + size[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : axis -
+//-----------------------------------------------------------------------------
+void BoundBox::Rotate90(int axis)
+{
+ int e1 = AXIS_X, e2 = AXIS_Y;
+
+ // get bounds center first
+ Vector center;
+ GetBoundsCenter(center);
+
+ switch(axis)
+ {
+ case AXIS_Z:
+ e1 = AXIS_X;
+ e2 = AXIS_Y;
+ break;
+ case AXIS_X:
+ e1 = AXIS_Y;
+ e2 = AXIS_Z;
+ break;
+ case AXIS_Y:
+ e1 = AXIS_X;
+ e2 = AXIS_Z;
+ break;
+ }
+
+ float tmp1, tmp2;
+ tmp1 = bmins[e1] - center[e1] + center[e2];
+ tmp2 = bmaxs[e1] - center[e1] + center[e2];
+ bmins[e1] = bmins[e2] - center[e2] + center[e1];
+ bmaxs[e1] = bmaxs[e2] - center[e2] + center[e1];
+ bmins[e2] = tmp1;
+ bmaxs[e2] = tmp2;
+}
+
diff --git a/mp/src/utils/vbsp/boundbox.h b/mp/src/utils/vbsp/boundbox.h new file mode 100644 index 00000000..c9838fe6 --- /dev/null +++ b/mp/src/utils/vbsp/boundbox.h @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: An axis aligned bounding box class.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BOUNDBOX_H
+#define BOUNDBOX_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "mathlib/vector.h"
+
+#define COORD_NOTINIT ((float)(99999.0))
+
+enum
+{
+ AXIS_X = 0,
+ AXIS_Y,
+ AXIS_Z
+};
+
+class BoundBox
+{
+ public:
+
+ BoundBox(void);
+ BoundBox(const Vector &mins, const Vector &maxs);
+
+ void ResetBounds(void);
+ inline void SetBounds(const Vector &mins, const Vector &maxs);
+
+ void UpdateBounds(const Vector& bmins, const Vector& bmaxs);
+ void UpdateBounds(const Vector& pt);
+ void UpdateBounds(const BoundBox *pBox);
+ void GetBoundsCenter(Vector& ptdest);
+ inline void GetBounds(Vector& Mins, Vector& Maxs);
+
+ virtual bool IsIntersectingBox(const Vector& pfMins, const Vector& pfMaxs) const;
+ bool IsInsideBox(const Vector& pfMins, const Vector& pfMaxs) const;
+ bool ContainsPoint(const Vector& pt) const;
+ bool IsValidBox(void) const;
+ void GetBoundsSize(Vector& size);
+ void SnapToGrid(int iGridSize);
+ void Rotate90(int axis);
+
+ Vector bmins;
+ Vector bmaxs;
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the bounding box as two vectors, a min and a max.
+// Input : Mins - Receives the box's minima.
+// Maxs - Receives the box's maxima.
+//-----------------------------------------------------------------------------
+void BoundBox::GetBounds(Vector &Mins, Vector &Maxs)
+{
+ Mins = bmins;
+ Maxs = bmaxs;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the box outright, equivalent to ResetBounds + UpdateBounds.
+// Input : mins - Minima to set.
+// maxs - Maxima to set.
+//-----------------------------------------------------------------------------
+void BoundBox::SetBounds(const Vector &mins, const Vector &maxs)
+{
+ bmins = mins;
+ bmaxs = maxs;
+}
+
+
+#endif // BOUNDBOX_H
diff --git a/mp/src/utils/vbsp/brushbsp.cpp b/mp/src/utils/vbsp/brushbsp.cpp new file mode 100644 index 00000000..17323c58 --- /dev/null +++ b/mp/src/utils/vbsp/brushbsp.cpp @@ -0,0 +1,1469 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+
+int c_nodes;
+int c_nonvis;
+int c_active_brushes;
+
+// if a brush just barely pokes onto the other side,
+// let it slide by without chopping
+#define PLANESIDE_EPSILON 0.001
+//0.1
+
+
+void FindBrushInTree (node_t *node, int brushnum)
+{
+ bspbrush_t *b;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ for (b=node->brushlist ; b ; b=b->next)
+ if (b->original->brushnum == brushnum)
+ Msg("here\n");
+ return;
+ }
+ FindBrushInTree (node->children[0], brushnum);
+ FindBrushInTree (node->children[1], brushnum);
+}
+
+//==================================================
+
+/*
+================
+DrawBrushList
+================
+*/
+void DrawBrushList (bspbrush_t *brush, node_t *node)
+{
+ int i;
+ side_t *s;
+
+ GLS_BeginScene ();
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ if (s->texinfo == TEXINFO_NODE)
+ GLS_Winding (s->winding, 1);
+ else if (!s->visible)
+ GLS_Winding (s->winding, 2);
+ else
+ GLS_Winding (s->winding, 0);
+ }
+ }
+ GLS_EndScene ();
+}
+
+/*
+================
+WriteBrushList
+================
+*/
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
+{
+ int i;
+ side_t *s;
+
+ qprintf ("writing %s\n", name);
+ FileHandle_t f = g_pFileSystem->Open(name, "w");
+
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ if (onlyvis && !s->visible)
+ continue;
+ OutputWinding (brush->sides[i].winding, f);
+ }
+ }
+
+ g_pFileSystem->Close (f);
+}
+
+void PrintBrush (bspbrush_t *brush)
+{
+ int i;
+
+ Msg("brush: %p\n", brush);
+ for (i=0;i<brush->numsides ; i++)
+ {
+ pw(brush->sides[i].winding);
+ Msg("\n");
+ }
+}
+
+/*
+==================
+BoundBrush
+
+Sets the mins/maxs based on the windings
+==================
+*/
+void BoundBrush (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+
+ ClearBounds (brush->mins, brush->maxs);
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ AddPointToBounds (w->p[j], brush->mins, brush->maxs);
+ }
+}
+
+Vector PointInsideBrush( bspbrush_t *brush )
+{
+ Vector insidePoint = vec3_origin;
+
+ bool bInside = false;
+ for ( int k = 0; k < 4 && !bInside; k++ )
+ {
+ bInside = true;
+ for (int i = 0; i < brush->numsides; i++)
+ {
+ side_t *side = &brush->sides[i];
+ plane_t *plane = &g_MainMap->mapplanes[side->planenum];
+ float d = DotProduct( plane->normal, insidePoint ) - plane->dist;
+ if ( d < 0 )
+ {
+ bInside = false;
+ insidePoint -= d * plane->normal;
+ }
+ }
+ }
+ return insidePoint;
+}
+
+/*
+==================
+CreateBrushWindings
+
+==================
+*/
+void CreateBrushWindings (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ // translate the CSG problem to improve precision
+ Vector insidePoint = PointInsideBrush( brush );
+ Vector offset = -insidePoint;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = &brush->sides[i];
+ plane = &g_MainMap->mapplanes[side->planenum];
+ w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal, offset));
+ for (j=0 ; j<brush->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ if (brush->sides[j].bevel)
+ continue;
+ plane = &g_MainMap->mapplanes[brush->sides[j].planenum^1];
+ ChopWindingInPlace (&w, plane->normal, plane->dist + DotProduct(plane->normal, offset), 0); //CLIP_EPSILON);
+ }
+
+ TranslateWinding( w, -offset );
+ side->winding = w;
+ }
+
+ BoundBrush (brush);
+}
+
+/*
+==================
+BrushFromBounds
+
+Creates a new axial brush
+==================
+*/
+bspbrush_t *BrushFromBounds (Vector& mins, Vector& maxs)
+{
+ bspbrush_t *b;
+ int i;
+ Vector normal;
+ vec_t dist;
+
+ b = AllocBrush (6);
+ b->numsides = 6;
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ dist = maxs[i];
+ b->sides[i].planenum = g_MainMap->FindFloatPlane (normal, dist);
+
+ normal[i] = -1;
+ dist = -mins[i];
+ b->sides[3+i].planenum = g_MainMap->FindFloatPlane (normal, dist);
+ }
+
+ CreateBrushWindings (b);
+
+ return b;
+}
+
+/*
+==================
+BrushVolume
+
+==================
+*/
+vec_t BrushVolume (bspbrush_t *brush)
+{
+ int i;
+ winding_t *w;
+ Vector corner;
+ vec_t d, area, volume;
+ plane_t *plane;
+
+ if (!brush)
+ return 0;
+
+ // grab the first valid point as the corner
+
+ w = NULL;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (w)
+ break;
+ }
+ if (!w)
+ return 0;
+ VectorCopy (w->p[0], corner);
+
+ // make tetrahedrons to all other faces
+
+ volume = 0;
+ for ( ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ plane = &g_MainMap->mapplanes[brush->sides[i].planenum];
+ d = -(DotProduct (corner, plane->normal) - plane->dist);
+ area = WindingArea (w);
+ volume += d*area;
+ }
+
+ volume /= 3;
+ return volume;
+}
+
+/*
+================
+CountBrushList
+================
+*/
+int CountBrushList (bspbrush_t *brushes)
+{
+ int c;
+
+ c = 0;
+ for ( ; brushes ; brushes = brushes->next)
+ c++;
+ return c;
+}
+
+/*
+================
+AllocTree
+================
+*/
+tree_t *AllocTree (void)
+{
+ tree_t *tree;
+
+ tree = (tree_t*)malloc(sizeof(*tree));
+ memset (tree, 0, sizeof(*tree));
+ ClearBounds (tree->mins, tree->maxs);
+
+ return tree;
+}
+
+/*
+================
+AllocNode
+================
+*/
+node_t *AllocNode (void)
+{
+ static int s_NodeCount = 0;
+
+ node_t *node;
+
+ node = (node_t*)malloc(sizeof(*node));
+ memset (node, 0, sizeof(*node));
+ node->id = s_NodeCount;
+ node->diskId = -1;
+
+ s_NodeCount++;
+
+ return node;
+}
+
+
+/*
+================
+AllocBrush
+================
+*/
+bspbrush_t *AllocBrush (int numsides)
+{
+ static int s_BrushId = 0;
+
+ bspbrush_t *bb;
+ int c;
+
+ c = (int)&(((bspbrush_t *)0)->sides[numsides]);
+ bb = (bspbrush_t*)malloc(c);
+ memset (bb, 0, c);
+ bb->id = s_BrushId++;
+ if (numthreads == 1)
+ c_active_brushes++;
+ return bb;
+}
+
+/*
+================
+FreeBrush
+================
+*/
+void FreeBrush (bspbrush_t *brushes)
+{
+ int i;
+
+ for (i=0 ; i<brushes->numsides ; i++)
+ if (brushes->sides[i].winding)
+ FreeWinding(brushes->sides[i].winding);
+ free (brushes);
+ if (numthreads == 1)
+ c_active_brushes--;
+}
+
+
+/*
+================
+FreeBrushList
+================
+*/
+void FreeBrushList (bspbrush_t *brushes)
+{
+ bspbrush_t *next;
+
+ for ( ; brushes ; brushes = next)
+ {
+ next = brushes->next;
+
+ FreeBrush (brushes);
+ }
+}
+
+/*
+==================
+CopyBrush
+
+Duplicates the brush, the sides, and the windings
+==================
+*/
+bspbrush_t *CopyBrush (bspbrush_t *brush)
+{
+ bspbrush_t *newbrush;
+ int size;
+ int i;
+
+ size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
+
+ newbrush = AllocBrush (brush->numsides);
+ memcpy (newbrush, brush, size);
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ if (brush->sides[i].winding)
+ newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
+ }
+
+ return newbrush;
+}
+
+
+/*
+==================
+PointInLeaf
+
+==================
+*/
+node_t *PointInLeaf (node_t *node, Vector& point)
+{
+ vec_t d;
+ plane_t *plane;
+
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &g_MainMap->mapplanes[node->planenum];
+ if (plane->type < 3)
+ {
+ d = point[plane->type] - plane->dist;
+ }
+ else
+ {
+ d = DotProduct (point, plane->normal) - plane->dist;
+ }
+
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return node;
+}
+
+//========================================================
+
+/*
+==============
+BoxOnPlaneSide
+
+Returns PSIDE_FRONT, PSIDE_BACK, or PSIDE_BOTH
+==============
+*/
+int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane)
+{
+ int side;
+ int i;
+ Vector corners[2];
+ vec_t dist1, dist2;
+
+ // axial planes are easy
+ if (plane->type < 3)
+ {
+ side = 0;
+ if (maxs[plane->type] > plane->dist+PLANESIDE_EPSILON)
+ side |= PSIDE_FRONT;
+ if (mins[plane->type] < plane->dist-PLANESIDE_EPSILON)
+ side |= PSIDE_BACK;
+ return side;
+ }
+
+ // create the proper leading and trailing verts for the box
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (plane->normal[i] < 0)
+ {
+ corners[0][i] = mins[i];
+ corners[1][i] = maxs[i];
+ }
+ else
+ {
+ corners[1][i] = mins[i];
+ corners[0][i] = maxs[i];
+ }
+ }
+
+ dist1 = DotProduct (plane->normal, corners[0]) - plane->dist;
+ dist2 = DotProduct (plane->normal, corners[1]) - plane->dist;
+ side = 0;
+ if (dist1 >= PLANESIDE_EPSILON)
+ side = PSIDE_FRONT;
+ if (dist2 < PLANESIDE_EPSILON)
+ side |= PSIDE_BACK;
+
+ return side;
+}
+
+/*
+============
+QuickTestBrushToPlanenum
+
+============
+*/
+int QuickTestBrushToPlanenum (bspbrush_t *brush, int planenum, int *numsplits)
+{
+ int i, num;
+ plane_t *plane;
+ int s;
+
+ *numsplits = 0;
+
+ // if the brush actually uses the planenum,
+ // we can tell the side for sure
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ num = brush->sides[i].planenum;
+ if (num >= 0x10000)
+ Error ("bad planenum");
+ if (num == planenum)
+ return PSIDE_BACK|PSIDE_FACING;
+ if (num == (planenum ^ 1) )
+ return PSIDE_FRONT|PSIDE_FACING;
+ }
+
+ // box on plane side
+ plane = &g_MainMap->mapplanes[planenum];
+ s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+ // if both sides, count the visible faces split
+ if (s == PSIDE_BOTH)
+ {
+ *numsplits += 3;
+ }
+
+ return s;
+}
+
+/*
+============
+TestBrushToPlanenum
+
+============
+*/
+int TestBrushToPlanenum (bspbrush_t *brush, int planenum,
+ int *numsplits, qboolean *hintsplit, int *epsilonbrush)
+{
+ int i, j, num;
+ plane_t *plane;
+ int s;
+ winding_t *w;
+ vec_t d, d_front, d_back;
+ int front, back;
+
+ *numsplits = 0;
+ *hintsplit = false;
+
+ // if the brush actually uses the planenum,
+ // we can tell the side for sure
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ num = brush->sides[i].planenum;
+ if (num >= 0x10000)
+ Error ("bad planenum");
+ if (num == planenum)
+ return PSIDE_BACK|PSIDE_FACING;
+ if (num == (planenum ^ 1) )
+ return PSIDE_FRONT|PSIDE_FACING;
+ }
+
+ // box on plane side
+ plane = &g_MainMap->mapplanes[planenum];
+ s = BrushBspBoxOnPlaneSide (brush->mins, brush->maxs, plane);
+
+ if (s != PSIDE_BOTH)
+ return s;
+
+// if both sides, count the visible faces split
+ d_front = d_back = 0;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ if (brush->sides[i].texinfo == TEXINFO_NODE)
+ continue; // on node, don't worry about splits
+ if (!brush->sides[i].visible)
+ continue; // we don't care about non-visible
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+
+ front = back = 0;
+ for (j=0 ; j<w->numpoints; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+
+ if (d > d_front)
+ d_front = d;
+ if (d < d_back)
+ d_back = d;
+
+ if (d > 0.1) // PLANESIDE_EPSILON)
+ front = 1;
+ if (d < -0.1) // PLANESIDE_EPSILON)
+ back = 1;
+ }
+
+ if (front && back)
+ {
+ if ( !(brush->sides[i].surf & SURF_SKIP) )
+ {
+ (*numsplits)++;
+ if (brush->sides[i].surf & SURF_HINT)
+ *hintsplit = true;
+ }
+ }
+ }
+
+ if ( (d_front > 0.0 && d_front < 1.0)
+ || (d_back < 0.0 && d_back > -1.0) )
+ (*epsilonbrush)++;
+
+#if 0
+ if (*numsplits == 0)
+ { // didn't really need to be split
+ if (front)
+ s = PSIDE_FRONT;
+ else if (back)
+ s = PSIDE_BACK;
+ else
+ s = 0;
+ }
+#endif
+
+ return s;
+}
+
+//========================================================
+
+/*
+================
+WindingIsTiny
+
+Returns true if the winding would be crunched out of
+existance by the vertex snapping.
+================
+*/
+#define EDGE_LENGTH 0.2
+qboolean WindingIsTiny (winding_t *w)
+{
+ int i, j;
+ vec_t len;
+ Vector delta;
+ int edges;
+
+ edges = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = i == w->numpoints - 1 ? 0 : i+1;
+ VectorSubtract (w->p[j], w->p[i], delta);
+ len = VectorLength (delta);
+ if (len > EDGE_LENGTH)
+ {
+ if (++edges == 3)
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// UNDONE: JAY: This should be a slightly better heuristic - it builds an OBB
+// around the winding and tests planar dimensions. NOTE: This can fail when a
+// winding normal cannot be constructed (or is degenerate), but that is probably
+// desired in this case.
+// UNDONE: Test & use this instead.
+#if 0
+qboolean WindingIsTiny2 (winding_t *w)
+{
+ int i, j;
+ vec_t len;
+ Vector delta;
+ int edges;
+
+ vec_t maxLen = 0;
+ Vector maxEdge = vec3_origin;
+
+ edges = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = i == w->numpoints - 1 ? 0 : i+1;
+ VectorSubtract (w->p[j], w->p[i], delta);
+ len = VectorLength (delta);
+ if (len > maxLen)
+ {
+ maxEdge = delta;
+ maxLen = len;
+ }
+ }
+ Vector normal;
+ vec_t dist;
+ WindingPlane (w, normal, &dist); // normal can come back vec3_origin in some cases
+ VectorNormalize(maxEdge);
+ Vector cross = CrossProduct(normal, maxEdge);
+ VectorNormalize(cross);
+ Vector mins, maxs;
+ ClearBounds( mins, maxs );
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ Vector point;
+ point.x = DotProduct( w->p[i], maxEdge );
+ point.y = DotProduct( w->p[i], cross );
+ point.z = DotProduct( w->p[i], normal );
+ AddPointToBounds( point, mins, maxs );
+ }
+
+ // check to see if the size in the plane is too small in either dimension
+ Vector size = maxs - mins;
+ for ( i = 0; i < 2; i++ )
+ {
+ if ( size[i] < EDGE_LENGTH )
+ return true;
+ }
+ return false;
+}
+#endif
+
+
+/*
+================
+WindingIsHuge
+
+Returns true if the winding still has one of the points
+from basewinding for plane
+================
+*/
+qboolean WindingIsHuge (winding_t *w)
+{
+ int i, j;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ if (w->p[i][j] < MIN_COORD_INTEGER || w->p[i][j] > MAX_COORD_INTEGER)
+ return true;
+ }
+ return false;
+}
+
+//============================================================
+
+/*
+================
+Leafnode
+================
+*/
+void LeafNode (node_t *node, bspbrush_t *brushes)
+{
+ bspbrush_t *b;
+ int i;
+
+ node->planenum = PLANENUM_LEAF;
+ node->contents = 0;
+
+ for (b=brushes ; b ; b=b->next)
+ {
+ // if the brush is solid and all of its sides are on nodes,
+ // it eats everything
+ if (b->original->contents & CONTENTS_SOLID)
+ {
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->sides[i].texinfo != TEXINFO_NODE)
+ break;
+ if (i == b->numsides)
+ {
+ node->contents = CONTENTS_SOLID;
+ break;
+ }
+ }
+ node->contents |= b->original->contents;
+ }
+
+ node->brushlist = brushes;
+}
+
+
+void RemoveAreaPortalBrushes_R( node_t *node )
+{
+ if( node->planenum == PLANENUM_LEAF )
+ {
+ // Remove any CONTENTS_AREAPORTAL brushes we added. We don't want them in the engine
+ // at runtime but we do want their flags in the leaves.
+ bspbrush_t **pPrev = &node->brushlist;
+ for( bspbrush_t *b=node->brushlist; b; b=b->next )
+ {
+ if( b->original->contents == CONTENTS_AREAPORTAL )
+ {
+ *pPrev = b->next;
+ }
+ else
+ {
+ pPrev = &b->next;
+ }
+ }
+ }
+ else
+ {
+ RemoveAreaPortalBrushes_R( node->children[0] );
+ RemoveAreaPortalBrushes_R( node->children[1] );
+ }
+}
+
+
+//============================================================
+
+void CheckPlaneAgainstParents (int pnum, node_t *node)
+{
+ node_t *p;
+
+ for (p=node->parent ; p ; p=p->parent)
+ {
+ if (p->planenum == pnum)
+ Error ("Tried parent");
+ }
+}
+
+qboolean CheckPlaneAgainstVolume (int pnum, node_t *node)
+{
+ bspbrush_t *front, *back;
+ qboolean good;
+
+ SplitBrush (node->volume, pnum, &front, &back);
+
+ good = (front && back);
+
+ if (front)
+ FreeBrush (front);
+ if (back)
+ FreeBrush (back);
+
+ return good;
+}
+
+/*
+================
+SelectSplitSide
+
+Using a hueristic, choses one of the sides out of the brushlist
+to partition the brushes with.
+Returns NULL if there are no valid planes to split with..
+================
+*/
+
+side_t *SelectSplitSide (bspbrush_t *brushes, node_t *node)
+{
+ int value, bestvalue;
+ bspbrush_t *brush, *test;
+ side_t *side, *bestside;
+ int i, j, pass, numpasses;
+ int pnum;
+ int s;
+ int front, back, both, facing, splits;
+ int bsplits;
+ int bestsplits;
+ int epsilonbrush;
+ qboolean hintsplit = false;
+
+ bestside = NULL;
+ bestvalue = -99999;
+ bestsplits = 0;
+
+ // the search order goes: visible-structural, nonvisible-structural
+ // If any valid plane is available in a pass, no further
+ // passes will be tried.
+ numpasses = 2;
+ for (pass = 0 ; pass < numpasses ; pass++)
+ {
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = brush->sides + i;
+
+ if (side->bevel)
+ continue; // never use a bevel as a spliter
+ if (!side->winding)
+ continue; // nothing visible, so it can't split
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // allready a node splitter
+ if (side->tested)
+ continue; // we allready have metrics for this plane
+ if (side->surf & SURF_SKIP)
+ continue; // skip surfaces are never chosen
+ if ( side->visible ^ (pass<1) )
+ continue; // only check visible faces on first pass
+
+ pnum = side->planenum;
+ pnum &= ~1; // allways use positive facing plane
+
+ CheckPlaneAgainstParents (pnum, node);
+
+ if (!CheckPlaneAgainstVolume (pnum, node))
+ continue; // would produce a tiny volume
+
+ front = 0;
+ back = 0;
+ both = 0;
+ facing = 0;
+ splits = 0;
+ epsilonbrush = 0;
+
+ for (test = brushes ; test ; test=test->next)
+ {
+ s = TestBrushToPlanenum (test, pnum, &bsplits, &hintsplit, &epsilonbrush);
+
+ splits += bsplits;
+ if (bsplits && (s&PSIDE_FACING) )
+ Error ("PSIDE_FACING with splits");
+
+ test->testside = s;
+ // if the brush shares this face, don't bother
+ // testing that facenum as a splitter again
+ if (s & PSIDE_FACING)
+ {
+ facing++;
+ for (j=0 ; j<test->numsides ; j++)
+ {
+ if ( (test->sides[j].planenum&~1) == pnum)
+ test->sides[j].tested = true;
+ }
+ }
+ if (s & PSIDE_FRONT)
+ front++;
+ if (s & PSIDE_BACK)
+ back++;
+ if (s == PSIDE_BOTH)
+ both++;
+ }
+
+ // give a value estimate for using this plane
+ value = 5*facing - 5*splits - abs(front-back);
+// value = -5*splits;
+// value = 5*facing - 5*splits;
+ if (g_MainMap->mapplanes[pnum].type < 3)
+ value+=5; // axial is better
+ value -= epsilonbrush*1000; // avoid!
+
+ // trans should split last
+ if ( side->surf & SURF_TRANS )
+ {
+ value -= 500;
+ }
+
+ // never split a hint side except with another hint
+ if (hintsplit && !(side->surf & SURF_HINT) )
+ value = -9999999;
+
+ // water should split first
+ if (side->contents & (CONTENTS_WATER | CONTENTS_SLIME))
+ value = 9999999;
+
+ // save off the side test so we don't need
+ // to recalculate it when we actually seperate
+ // the brushes
+ if (value > bestvalue)
+ {
+ bestvalue = value;
+ bestside = side;
+ bestsplits = splits;
+ for (test = brushes ; test ; test=test->next)
+ test->side = test->testside;
+ }
+ }
+ }
+
+ // if we found a good plane, don't bother trying any
+ // other passes
+ if (bestside)
+ {
+ if (pass > 0)
+ {
+ if (numthreads == 1)
+ c_nonvis++;
+ }
+ break;
+ }
+ }
+
+ //
+ // clear all the tested flags we set
+ //
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ brush->sides[i].tested = false;
+ }
+
+ return bestside;
+}
+
+
+/*
+==================
+BrushMostlyOnSide
+
+==================
+*/
+int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
+{
+ int i, j;
+ winding_t *w;
+ vec_t d, max;
+ int side;
+
+ max = 0;
+ side = PSIDE_FRONT;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > max)
+ {
+ max = d;
+ side = PSIDE_FRONT;
+ }
+ if (-d > max)
+ {
+ max = -d;
+ side = PSIDE_BACK;
+ }
+ }
+ }
+ return side;
+}
+
+/*
+================
+SplitBrush
+
+Generates two new brushes, leaving the original
+unchanged
+================
+*/
+
+
+void SplitBrush( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
+{
+ bspbrush_t *b[2];
+ int i, j;
+ winding_t *w, *cw[2], *midwinding;
+ plane_t *plane, *plane2;
+ side_t *s, *cs;
+ float d, d_front, d_back;
+
+ *front = *back = NULL;
+ plane = &g_MainMap->mapplanes[planenum];
+
+ // check all points
+ d_front = d_back = 0;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > 0 && d > d_front)
+ d_front = d;
+ if (d < 0 && d < d_back)
+ d_back = d;
+ }
+ }
+
+ if (d_front < 0.1) // PLANESIDE_EPSILON)
+ { // only on back
+ *back = CopyBrush (brush);
+ return;
+ }
+ if (d_back > -0.1) // PLANESIDE_EPSILON)
+ { // only on front
+ *front = CopyBrush (brush);
+ return;
+ }
+
+
+ // Move the CSG problem so that offset is at the origin
+ // This gives us much better floating point precision in the clipping operations
+ Vector offset = -0.5f * (brush->mins + brush->maxs);
+ // create a new winding from the split plane
+
+ w = BaseWindingForPlane (plane->normal, plane->dist + DotProduct(plane->normal,offset));
+ for (i=0 ; i<brush->numsides && w ; i++)
+ {
+ plane2 = &g_MainMap->mapplanes[brush->sides[i].planenum ^ 1];
+ ChopWindingInPlace (&w, plane2->normal, plane2->dist+DotProduct(plane2->normal,offset), 0); // PLANESIDE_EPSILON);
+ }
+
+ if (!w || WindingIsTiny (w) )
+ { // the brush isn't really split
+ int side;
+
+ side = BrushMostlyOnSide (brush, plane);
+ if (side == PSIDE_FRONT)
+ *front = CopyBrush (brush);
+ if (side == PSIDE_BACK)
+ *back = CopyBrush (brush);
+ return;
+ }
+
+ if (WindingIsHuge (w))
+ {
+ qprintf ("WARNING: huge winding\n");
+ }
+
+ TranslateWinding( w, -offset );
+ midwinding = w;
+
+ //
+ //
+ // split it for real
+ //
+ //
+
+ //
+ // allocate two new brushes referencing the original
+ //
+ for( i = 0; i < 2; i++ )
+ {
+ b[i] = AllocBrush( brush->numsides + 1 );
+ b[i]->original = brush->original;
+ }
+
+ //
+ // split all the current windings
+ //
+ for( i = 0; i < brush->numsides; i++ )
+ {
+ // get the current side
+ s = &brush->sides[i];
+
+ // get the sides winding
+ w = s->winding;
+ if( !w )
+ continue;
+
+ // clip the winding
+ ClipWindingEpsilon_Offset( w, plane->normal, plane->dist, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1], offset );
+
+ for( j = 0; j < 2; j++ )
+ {
+ // does winding exist?
+ if( !cw[j] )
+ continue;
+#if 0
+ if (WindingIsTiny (cw[j]))
+ {
+ FreeWinding (cw[j]);
+ continue;
+ }
+#endif
+
+ //
+ // create a clipped "side" with the new winding
+ //
+ cs = &b[j]->sides[b[j]->numsides];
+ b[j]->numsides++;
+ *cs = *s;
+ cs->winding = cw[j];
+ cs->tested = false;
+ // save the original side information
+ //cs->original = s->original;
+ }
+ }
+
+
+ // see if we have valid polygons on both sides
+
+ for (i=0 ; i<2 ; i++)
+ {
+ BoundBrush (b[i]);
+ for (j=0 ; j<3 ; j++)
+ {
+ if (b[i]->mins[j] < MIN_COORD_INTEGER || b[i]->maxs[j] > MAX_COORD_INTEGER)
+ {
+ qprintf ("bogus brush after clip\n");
+ break;
+ }
+ }
+
+ if (b[i]->numsides < 3 || j < 3)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+ }
+ }
+
+ if ( !(b[0] && b[1]) )
+ {
+ if (!b[0] && !b[1])
+ qprintf ("split removed brush\n");
+ else
+ qprintf ("split not on both sides\n");
+ if (b[0])
+ {
+ FreeBrush (b[0]);
+ *front = CopyBrush (brush);
+ }
+ if (b[1])
+ {
+ FreeBrush (b[1]);
+ *back = CopyBrush (brush);
+ }
+ return;
+ }
+
+ // add the midwinding to both sides
+ for (i=0 ; i<2 ; i++)
+ {
+ cs = &b[i]->sides[b[i]->numsides];
+ b[i]->numsides++;
+
+ cs->planenum = planenum^i^1;
+ cs->texinfo = TEXINFO_NODE;
+
+ // initialize the displacement map index
+ cs->pMapDisp = NULL;
+
+ cs->visible = false;
+ cs->tested = false;
+ if (i==0)
+ cs->winding = CopyWinding (midwinding);
+ else
+ cs->winding = midwinding;
+ }
+
+{
+ vec_t v1;
+ int i;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ v1 = BrushVolume (b[i]);
+ if (v1 < 1.0)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+// qprintf ("tiny volume after clip\n");
+ }
+ }
+}
+
+ *front = b[0];
+ *back = b[1];
+}
+
+
+/*
+================
+SplitBrushList
+================
+*/
+void SplitBrushList (bspbrush_t *brushes,
+ node_t *node, bspbrush_t **front, bspbrush_t **back)
+{
+ bspbrush_t *brush, *newbrush, *newbrush2;
+ side_t *side;
+ int sides;
+ int i;
+
+ *front = *back = NULL;
+
+ for (brush = brushes ; brush ; brush=brush->next)
+ {
+ sides = brush->side;
+
+ if (sides == PSIDE_BOTH)
+ { // split into two brushes
+ SplitBrush (brush, node->planenum, &newbrush, &newbrush2);
+ if (newbrush)
+ {
+ newbrush->next = *front;
+ *front = newbrush;
+ }
+ if (newbrush2)
+ {
+ newbrush2->next = *back;
+ *back = newbrush2;
+ }
+ continue;
+ }
+
+ newbrush = CopyBrush (brush);
+
+ // if the planenum is actualy a part of the brush
+ // find the plane and flag it as used so it won't be tried
+ // as a splitter again
+ if (sides & PSIDE_FACING)
+ {
+ for (i=0 ; i<newbrush->numsides ; i++)
+ {
+ side = newbrush->sides + i;
+ if ( (side->planenum& ~1) == node->planenum)
+ side->texinfo = TEXINFO_NODE;
+ }
+ }
+
+
+ if (sides & PSIDE_FRONT)
+ {
+ newbrush->next = *front;
+ *front = newbrush;
+ continue;
+ }
+ if (sides & PSIDE_BACK)
+ {
+ newbrush->next = *back;
+ *back = newbrush;
+ continue;
+ }
+ }
+}
+
+
+/*
+================
+BuildTree_r
+================
+*/
+
+
+node_t *BuildTree_r (node_t *node, bspbrush_t *brushes)
+{
+ node_t *newnode;
+ side_t *bestside;
+ int i;
+ bspbrush_t *children[2];
+
+ if (numthreads == 1)
+ c_nodes++;
+
+ // find the best plane to use as a splitter
+ bestside = SelectSplitSide (brushes, node);
+
+ if (!bestside)
+ {
+ // leaf node
+ node->side = NULL;
+ node->planenum = -1;
+ LeafNode (node, brushes);
+ return node;
+ }
+
+ // this is a splitplane node
+ node->side = bestside;
+ node->planenum = bestside->planenum & ~1; // always use front facing
+
+ SplitBrushList (brushes, node, &children[0], &children[1]);
+ FreeBrushList (brushes);
+
+ // allocate children before recursing
+ for (i=0 ; i<2 ; i++)
+ {
+ newnode = AllocNode ();
+ newnode->parent = node;
+ node->children[i] = newnode;
+ }
+
+ SplitBrush (node->volume, node->planenum, &node->children[0]->volume,
+ &node->children[1]->volume);
+
+ // recursively process children
+ for (i=0 ; i<2 ; i++)
+ {
+ node->children[i] = BuildTree_r (node->children[i], children[i]);
+ }
+
+ return node;
+}
+
+
+//===========================================================
+
+/*
+=================
+BrushBSP
+
+The incoming list will be freed before exiting
+=================
+*/
+tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs)
+{
+ node_t *node;
+ bspbrush_t *b;
+ int c_faces, c_nonvisfaces;
+ int c_brushes;
+ tree_t *tree;
+ int i;
+ vec_t volume;
+
+ qprintf ("--- BrushBSP ---\n");
+
+ tree = AllocTree ();
+
+ c_faces = 0;
+ c_nonvisfaces = 0;
+ c_brushes = 0;
+ for (b=brushlist ; b ; b=b->next)
+ {
+ c_brushes++;
+
+ volume = BrushVolume (b);
+ if (volume < microvolume)
+ {
+ Warning("Brush %i: WARNING, microbrush\n", b->original->id);
+ }
+
+ for (i=0 ; i<b->numsides ; i++)
+ {
+ if (b->sides[i].bevel)
+ continue;
+ if (!b->sides[i].winding)
+ continue;
+ if (b->sides[i].texinfo == TEXINFO_NODE)
+ continue;
+ if (b->sides[i].visible)
+ c_faces++;
+ else
+ c_nonvisfaces++;
+ }
+
+ AddPointToBounds (b->mins, tree->mins, tree->maxs);
+ AddPointToBounds (b->maxs, tree->mins, tree->maxs);
+ }
+
+ qprintf ("%5i brushes\n", c_brushes);
+ qprintf ("%5i visible faces\n", c_faces);
+ qprintf ("%5i nonvisible faces\n", c_nonvisfaces);
+
+ c_nodes = 0;
+ c_nonvis = 0;
+ node = AllocNode ();
+
+ node->volume = BrushFromBounds (mins, maxs);
+
+ tree->headnode = node;
+
+ node = BuildTree_r (node, brushlist);
+ qprintf ("%5i visible nodes\n", c_nodes/2 - c_nonvis);
+ qprintf ("%5i nonvis nodes\n", c_nonvis);
+ qprintf ("%5i leafs\n", (c_nodes+1)/2);
+#if 0
+{ // debug code
+static node_t *tnode;
+Vector p;
+
+p[0] = -1469;
+p[1] = -118;
+p[2] = 119;
+tnode = PointInLeaf (tree->headnode, p);
+Msg("contents: %i\n", tnode->contents);
+p[0] = 0;
+}
+#endif
+ return tree;
+}
+
diff --git a/mp/src/utils/vbsp/csg.cpp b/mp/src/utils/vbsp/csg.cpp new file mode 100644 index 00000000..c4b89bd9 --- /dev/null +++ b/mp/src/utils/vbsp/csg.cpp @@ -0,0 +1,784 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+/*
+
+tag all brushes with original contents
+brushes may contain multiple contents
+there will be no brush overlap after csg phase
+
+
+
+
+each side has a count of the other sides it splits
+
+the best split will be the one that minimizes the total split counts
+of all remaining sides
+
+precalc side on plane table
+
+evaluate split side
+{
+cost = 0
+for all sides
+ for all sides
+ get
+ if side splits side and splitside is on same child
+ cost++;
+}
+
+
+ */
+
+void SplitBrush2( bspbrush_t *brush, int planenum, bspbrush_t **front, bspbrush_t **back )
+{
+ SplitBrush( brush, planenum, front, back );
+#if 0
+ if (*front && (*front)->sides[(*front)->numsides-1].texinfo == -1)
+ (*front)->sides[(*front)->numsides-1].texinfo = (*front)->sides[0].texinfo; // not -1
+ if (*back && (*back)->sides[(*back)->numsides-1].texinfo == -1)
+ (*back)->sides[(*back)->numsides-1].texinfo = (*back)->sides[0].texinfo; // not -1
+#endif
+}
+
+/*
+===============
+SubtractBrush
+
+Returns a list of brushes that remain after B is subtracted from A.
+May by empty if A is contained inside B.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *SubtractBrush (bspbrush_t *a, bspbrush_t *b)
+{ // a - b = out (list)
+ int i;
+ bspbrush_t *front, *back;
+ bspbrush_t *out, *in;
+
+ in = a;
+ out = NULL;
+ for (i=0 ; i<b->numsides && in ; i++)
+ {
+ SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+ if (in != a)
+ FreeBrush (in);
+ if (front)
+ { // add to list
+ front->next = out;
+ out = front;
+ }
+ in = back;
+ }
+ if (in)
+ FreeBrush (in);
+ else
+ { // didn't really intersect
+ FreeBrushList (out);
+ return a;
+ }
+ return out;
+}
+
+/*
+===============
+IntersectBrush
+
+Returns a single brush made up by the intersection of the
+two provided brushes, or NULL if they are disjoint.
+
+The originals are undisturbed.
+===============
+*/
+bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b)
+{
+ int i;
+ bspbrush_t *front, *back;
+ bspbrush_t *in;
+
+ in = a;
+ for (i=0 ; i<b->numsides && in ; i++)
+ {
+ SplitBrush2 (in, b->sides[i].planenum, &front, &back);
+ if (in != a)
+ FreeBrush (in);
+ if (front)
+ FreeBrush (front);
+ in = back;
+ }
+
+ if (in == a || !in)
+ return NULL;
+
+ in->next = NULL;
+ return in;
+}
+
+
+/*
+===============
+BrushesDisjoint
+
+Returns true if the two brushes definately do not intersect.
+There will be false negatives for some non-axial combinations.
+===============
+*/
+qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b)
+{
+ int i, j;
+
+ // check bounding boxes
+ for (i=0 ; i<3 ; i++)
+ if (a->mins[i] >= b->maxs[i]
+ || a->maxs[i] <= b->mins[i])
+ return true; // bounding boxes don't overlap
+
+ // check for opposing planes
+ for (i=0 ; i<a->numsides ; i++)
+ {
+ for (j=0 ; j<b->numsides ; j++)
+ {
+ if (a->sides[i].planenum ==
+ (b->sides[j].planenum^1) )
+ return true; // opposite planes, so not touching
+ }
+ }
+
+ return false; // might intersect
+}
+
+
+int minplanenums[3];
+int maxplanenums[3];
+
+/*
+===============
+ClipBrushToBox
+
+Any planes shared with the box edge will be set to no texinfo
+===============
+*/
+bspbrush_t *ClipBrushToBox (bspbrush_t *brush, const Vector& clipmins, const Vector& clipmaxs)
+{
+ int i, j;
+ bspbrush_t *front, *back;
+ int p;
+
+ for (j=0 ; j<2 ; j++)
+ {
+ if (brush->maxs[j] > clipmaxs[j])
+ {
+ SplitBrush (brush, maxplanenums[j], &front, &back);
+ if (front)
+ FreeBrush (front);
+ brush = back;
+ if (!brush)
+ return NULL;
+ }
+ if (brush->mins[j] < clipmins[j])
+ {
+ SplitBrush (brush, minplanenums[j], &front, &back);
+ if (back)
+ FreeBrush (back);
+ brush = front;
+ if (!brush)
+ return NULL;
+ }
+ }
+
+ // remove any colinear faces
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ p = brush->sides[i].planenum & ~1;
+ if (p == maxplanenums[0] || p == maxplanenums[1]
+ || p == minplanenums[0] || p == minplanenums[1])
+ {
+ brush->sides[i].texinfo = TEXINFO_NODE;
+ brush->sides[i].visible = false;
+ }
+ }
+ return brush;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a clipped brush from a map brush
+//-----------------------------------------------------------------------------
+static bspbrush_t *CreateClippedBrush( mapbrush_t *mb, const Vector& clipmins, const Vector& clipmaxs )
+{
+ int nNumSides = mb->numsides;
+ if (!nNumSides)
+ return NULL;
+
+ // if the brush is outside the clip area, skip it
+ for (int j=0 ; j<3 ; j++)
+ {
+ if (mb->mins[j] >= clipmaxs[j] || mb->maxs[j] <= clipmins[j])
+ {
+ return NULL;
+ }
+ }
+
+ // make a copy of the brush
+ bspbrush_t *newbrush = AllocBrush( nNumSides );
+ newbrush->original = mb;
+ newbrush->numsides = nNumSides;
+ memcpy (newbrush->sides, mb->original_sides, nNumSides*sizeof(side_t));
+
+ for (int j=0 ; j<nNumSides; j++)
+ {
+ if (newbrush->sides[j].winding)
+ {
+ newbrush->sides[j].winding = CopyWinding (newbrush->sides[j].winding);
+ }
+
+ if (newbrush->sides[j].surf & SURF_HINT)
+ {
+ newbrush->sides[j].visible = true; // hints are always visible
+ }
+
+ // keep a pointer to the original map brush side -- use to create the original face later!!
+ //newbrush->sides[j].original = &mb->original_sides[j];
+ }
+
+ VectorCopy (mb->mins, newbrush->mins);
+ VectorCopy (mb->maxs, newbrush->maxs);
+
+ // carve off anything outside the clip box
+ newbrush = ClipBrushToBox (newbrush, clipmins, clipmaxs);
+ return newbrush;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a clipped brush from a map brush
+//-----------------------------------------------------------------------------
+static void ComputeBoundingPlanes( const Vector& clipmins, const Vector& clipmaxs )
+{
+ Vector normal;
+ float dist;
+ for (int i=0 ; i<2 ; i++)
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ dist = clipmaxs[i];
+ maxplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
+ dist = clipmins[i];
+ minplanenums[i] = g_MainMap->FindFloatPlane (normal, dist);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// This forces copies of texinfo data for matching sides of a brush
+//-----------------------------------------------------------------------------
+void CopyMatchingTexinfos( side_t *pDestSides, int numDestSides, const bspbrush_t *pSource )
+{
+ for ( int i = 0; i < numDestSides; i++ )
+ {
+ side_t *pSide = &pDestSides[i];
+ plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
+
+ // We have to use the *original sides* because MapBSPBrushList could have generated
+ // splits when cutting the original brush to the block being processed. This
+ // will generate faces that use TEXINFO_NODE, which is definitely *not* what we want.
+ // If we end up with faces using TEXINFO_NODE here, the area portal will flood into
+ // the entire water volume intersecting the areaportal.
+
+ mapbrush_t *pSourceBrush = pSource->original;
+ Assert( pSourceBrush );
+
+ const side_t *pSourceSide = pSourceBrush->original_sides;
+ const side_t *pBestSide = NULL;
+ float flBestDot = -1.0f;
+ for ( int j = 0; j < pSourceBrush->numsides; ++j, ++pSourceSide )
+ {
+ if ( pSourceSide->texinfo == TEXINFO_NODE )
+ continue;
+
+ plane_t *pSourcePlane = &g_MainMap->mapplanes[pSourceSide->planenum];
+ float flDot = DotProduct( pPlane->normal, pSourcePlane->normal );
+ if ( flDot == 1.0f || pSide->planenum == pSourceSide->planenum )
+ {
+ pBestSide = pSourceSide;
+ break;
+ }
+ else if ( flDot > flBestDot )
+ {
+ pBestSide = pSourceSide;
+ flBestDot = flDot;
+ }
+ }
+
+ if ( pBestSide )
+ {
+ pSide->texinfo = pBestSide->texinfo;
+ if ( pSide->original )
+ {
+ pSide->original->texinfo = pSide->texinfo;
+ }
+ }
+ else
+ {
+ texinfo_t *pTexInfo = &texinfo[pSide->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ Msg("Found no matching plane for %s\n", TexDataStringTable_GetString( pTexData->nameStringTableID ) );
+ }
+ }
+}
+
+// This is a hack to allow areaportals to work in water
+// It was done this way for ease of implementation.
+// This searches a brush list to find intersecting areaportals and water
+// If an areaportal is found inside water, then the water contents and
+// texture information is copied over to the areaportal so that the
+// resulting space has the same properties as the water (normal areaportals assume "empty" surroundings)
+void FixupAreaportalWaterBrushes( bspbrush_t *pList )
+{
+ for ( bspbrush_t *pAreaportal = pList; pAreaportal; pAreaportal = pAreaportal->next )
+ {
+ if ( !(pAreaportal->original->contents & CONTENTS_AREAPORTAL) )
+ continue;
+
+ for ( bspbrush_t *pWater = pList; pWater; pWater = pWater->next )
+ {
+ // avoid using areaportal/water combo brushes that have already been fixed up
+ if ( pWater->original->contents & CONTENTS_AREAPORTAL )
+ continue;
+
+ if ( !(pWater->original->contents & MASK_SPLITAREAPORTAL) )
+ continue;
+
+ if ( BrushesDisjoint( pAreaportal, pWater ) )
+ continue;
+
+ bspbrush_t *pIntersect = IntersectBrush( pAreaportal, pWater );
+ if ( !pIntersect )
+ continue;
+ FreeBrush( pIntersect );
+ pAreaportal->original->contents |= pWater->original->contents;
+
+ // HACKHACK: Ideally, this should have been done before the bspbrush_t was
+ // created from the map brush. But since it hasn't been, retexture the original map
+ // brush's sides
+ CopyMatchingTexinfos( pAreaportal->sides, pAreaportal->numsides, pWater );
+ CopyMatchingTexinfos( pAreaportal->original->original_sides, pAreaportal->original->numsides, pWater );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// MakeBspBrushList
+//-----------------------------------------------------------------------------
+// UNDONE: Put detail brushes in a separate brush array and pass that instead of "onlyDetail" ?
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush, const Vector& clipmins, const Vector& clipmaxs, int detailScreen)
+{
+ ComputeBoundingPlanes( clipmins, clipmaxs );
+
+ bspbrush_t *pBrushList = NULL;
+
+ int i;
+ for (i=startbrush ; i<endbrush ; i++)
+ {
+ mapbrush_t *mb = &g_MainMap->mapbrushes[i];
+ if ( detailScreen != FULL_DETAIL )
+ {
+ bool onlyDetail = (detailScreen == ONLY_DETAIL);
+ bool detail = (mb->contents & CONTENTS_DETAIL) != 0;
+ if ( onlyDetail ^ detail )
+ {
+ // both of these must have the same value or we're not interested in this brush
+ continue;
+ }
+ }
+
+ bspbrush_t *pNewBrush = CreateClippedBrush( mb, clipmins, clipmaxs );
+ if ( pNewBrush )
+ {
+ pNewBrush->next = pBrushList;
+ pBrushList = pNewBrush;
+ }
+ }
+
+ return pBrushList;
+}
+
+
+//-----------------------------------------------------------------------------
+// A version which uses a passed-in list of brushes
+//-----------------------------------------------------------------------------
+bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs)
+{
+ ComputeBoundingPlanes( clipmins, clipmaxs );
+
+ bspbrush_t *pBrushList = NULL;
+ for ( int i=0; i < nBrushCount; ++i )
+ {
+ bspbrush_t *pNewBrush = CreateClippedBrush( pBrushes[i], clipmins, clipmaxs );
+ if ( pNewBrush )
+ {
+ pNewBrush->next = pBrushList;
+ pBrushList = pNewBrush;
+ }
+ }
+
+ return pBrushList;
+}
+
+
+/*
+===============
+AddBspBrushListToTail
+===============
+*/
+bspbrush_t *AddBrushListToTail (bspbrush_t *list, bspbrush_t *tail)
+{
+ bspbrush_t *walk, *next;
+
+ for (walk=list ; walk ; walk=next)
+ { // add to end of list
+ next = walk->next;
+ walk->next = NULL;
+ tail->next = walk;
+ tail = walk;
+ }
+
+ return tail;
+}
+
+/*
+===========
+CullList
+
+Builds a new list that doesn't hold the given brush
+===========
+*/
+bspbrush_t *CullList (bspbrush_t *list, bspbrush_t *skip1)
+{
+ bspbrush_t *newlist;
+ bspbrush_t *next;
+
+ newlist = NULL;
+
+ for ( ; list ; list = next)
+ {
+ next = list->next;
+ if (list == skip1)
+ {
+ FreeBrush (list);
+ continue;
+ }
+ list->next = newlist;
+ newlist = list;
+ }
+ return newlist;
+}
+
+
+/*
+==================
+WriteBrushMap
+==================
+*/
+void WriteBrushMap (char *name, bspbrush_t *list)
+{
+ FILE *f;
+ side_t *s;
+ int i;
+ winding_t *w;
+
+ Msg("writing %s\n", name);
+ f = fopen (name, "w");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+
+ for ( ; list ; list=list->next )
+ {
+ fprintf (f, "{\n");
+ for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
+ {
+ w = BaseWindingForPlane (g_MainMap->mapplanes[s->planenum].normal, g_MainMap->mapplanes[s->planenum].dist);
+
+ fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+ fprintf (f, "%s 0 0 0 1 1\n", TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
+ FreeWinding (w);
+ }
+ fprintf (f, "}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+}
+
+// UNDONE: This isn't quite working yet
+#if 0
+void WriteBrushVMF(char *name, bspbrush_t *list)
+{
+ FILE *f;
+ side_t *s;
+ int i;
+ winding_t *w;
+ Vector u, v;
+
+ Msg("writing %s\n", name);
+ f = fopen (name, "w");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "world\n{\n\"classname\" \"worldspawn\"\n");
+
+ for ( ; list ; list=list->next )
+ {
+ fprintf (f, "\tsolid\n\t{\n");
+ for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
+ {
+ fprintf( f, "\t\tside\n\t\t{\n" );
+ fprintf( f, "\t\t\t\"plane\" \"" );
+ w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
+
+ fprintf (f,"(%i %i %i) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"(%i %i %i) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"(%i %i %i)", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+ fprintf( f, "\"\n" );
+ fprintf( f, "\t\t\t\"material\" \"%s\"\n", GetTexData( texinfo[s->texinfo].texdata )->name );
+ // UNDONE: recreate correct texture axes
+ BasisForPlane( mapplanes[s->planenum].normal, u, v );
+ fprintf( f, "\t\t\t\"uaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", u[0], u[1], u[2] );
+ fprintf( f, "\t\t\t\"vaxis\" \"[%.3f %.3f %.3f 0] 1.0\"\n", v[0], v[1], v[2] );
+
+ fprintf( f, "\t\t\t\"rotation\" \"0.0\"\n" );
+ fprintf( f, "\t\t\t\"lightmapscale\" \"16.0\"\n" );
+
+ FreeWinding (w);
+ fprintf (f, "\t\t}\n");
+ }
+ fprintf (f, "\t}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+}
+#endif
+
+void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars )
+{
+ #define ADD_CONTENTS( flag ) \
+ if ( contents & flag ) \
+ Q_strncat( pOut, #flag " ", nMaxChars, COPY_ALL_CHARACTERS );
+
+ pOut[0] = 0;
+
+ ADD_CONTENTS(CONTENTS_SOLID)
+ ADD_CONTENTS(CONTENTS_WINDOW)
+ ADD_CONTENTS(CONTENTS_AUX)
+ ADD_CONTENTS(CONTENTS_GRATE)
+ ADD_CONTENTS(CONTENTS_SLIME)
+ ADD_CONTENTS(CONTENTS_WATER)
+ ADD_CONTENTS(CONTENTS_BLOCKLOS)
+ ADD_CONTENTS(CONTENTS_OPAQUE)
+ ADD_CONTENTS(CONTENTS_TESTFOGVOLUME)
+ ADD_CONTENTS(CONTENTS_MOVEABLE)
+ ADD_CONTENTS(CONTENTS_AREAPORTAL)
+ ADD_CONTENTS(CONTENTS_PLAYERCLIP)
+ ADD_CONTENTS(CONTENTS_MONSTERCLIP)
+ ADD_CONTENTS(CONTENTS_CURRENT_0)
+ ADD_CONTENTS(CONTENTS_CURRENT_90)
+ ADD_CONTENTS(CONTENTS_CURRENT_180)
+ ADD_CONTENTS(CONTENTS_CURRENT_270)
+ ADD_CONTENTS(CONTENTS_CURRENT_UP)
+ ADD_CONTENTS(CONTENTS_CURRENT_DOWN)
+ ADD_CONTENTS(CONTENTS_ORIGIN)
+ ADD_CONTENTS(CONTENTS_MONSTER)
+ ADD_CONTENTS(CONTENTS_DEBRIS)
+ ADD_CONTENTS(CONTENTS_DETAIL)
+ ADD_CONTENTS(CONTENTS_TRANSLUCENT)
+ ADD_CONTENTS(CONTENTS_LADDER)
+ ADD_CONTENTS(CONTENTS_HITBOX)
+}
+
+void PrintBrushContents( int contents )
+{
+ char str[1024];
+ PrintBrushContentsToString( contents, str, sizeof( str ) );
+ Msg( "%s", str );
+}
+
+/*
+==================
+BrushGE
+
+Returns true if b1 is allowed to bite b2
+==================
+*/
+qboolean BrushGE (bspbrush_t *b1, bspbrush_t *b2)
+{
+ // Areaportals are allowed to bite water + slime
+ // NOTE: This brush combo should have been fixed up
+ // in a first pass (FixupAreaportalWaterBrushes)
+ if( (b2->original->contents & MASK_SPLITAREAPORTAL) &&
+ (b1->original->contents & CONTENTS_AREAPORTAL) )
+ {
+ return true;
+ }
+
+ // detail brushes never bite structural brushes
+ if ( (b1->original->contents & CONTENTS_DETAIL)
+ && !(b2->original->contents & CONTENTS_DETAIL) )
+ return false;
+ if (b1->original->contents & CONTENTS_SOLID)
+ return true;
+ // Transparent brushes are not marked as detail anymore, so let them cut each other.
+ if ( (b1->original->contents & TRANSPARENT_CONTENTS) && (b2->original->contents & TRANSPARENT_CONTENTS) )
+ return true;
+
+ return false;
+}
+
+/*
+=================
+ChopBrushes
+
+Carves any intersecting solid brushes into the minimum number
+of non-intersecting brushes.
+=================
+*/
+bspbrush_t *ChopBrushes (bspbrush_t *head)
+{
+ bspbrush_t *b1, *b2, *next;
+ bspbrush_t *tail;
+ bspbrush_t *keep;
+ bspbrush_t *sub, *sub2;
+ int c1, c2;
+
+ qprintf ("---- ChopBrushes ----\n");
+ qprintf ("original brushes: %i\n", CountBrushList (head));
+
+#if DEBUG_BRUSHMODEL
+ if (entity_num == DEBUG_BRUSHMODEL)
+ WriteBrushList ("before.gl", head, false);
+#endif
+ keep = NULL;
+
+newlist:
+ // find tail
+ if (!head)
+ return NULL;
+ for (tail=head ; tail->next ; tail=tail->next)
+ ;
+
+ for (b1=head ; b1 ; b1=next)
+ {
+ next = b1->next;
+ for (b2=b1->next ; b2 ; b2 = b2->next)
+ {
+ if (BrushesDisjoint (b1, b2))
+ continue;
+
+ sub = NULL;
+ sub2 = NULL;
+ c1 = 999999;
+ c2 = 999999;
+
+ if ( BrushGE (b2, b1) )
+ {
+// printf( "b2 bites b1\n" );
+ sub = SubtractBrush (b1, b2);
+ if (sub == b1)
+ continue; // didn't really intersect
+ if (!sub)
+ { // b1 is swallowed by b2
+ head = CullList (b1, b1);
+ goto newlist;
+ }
+ c1 = CountBrushList (sub);
+ }
+
+ if ( BrushGE (b1, b2) )
+ {
+// printf( "b1 bites b2\n" );
+ sub2 = SubtractBrush (b2, b1);
+ if (sub2 == b2)
+ continue; // didn't really intersect
+ if (!sub2)
+ { // b2 is swallowed by b1
+ FreeBrushList (sub);
+ head = CullList (b1, b2);
+ goto newlist;
+ }
+ c2 = CountBrushList (sub2);
+ }
+
+ if (!sub && !sub2)
+ continue; // neither one can bite
+
+ // only accept if it didn't fragment
+ // (commening this out allows full fragmentation)
+ if (c1 > 1 && c2 > 1)
+ {
+ const int contents1 = b1->original->contents;
+ const int contents2 = b2->original->contents;
+ // if both detail, allow fragmentation
+ if ( !((contents1&contents2) & CONTENTS_DETAIL) && !((contents1|contents2) & CONTENTS_AREAPORTAL) )
+ {
+ if (sub2)
+ FreeBrushList (sub2);
+ if (sub)
+ FreeBrushList (sub);
+ continue;
+ }
+ }
+
+ if (c1 < c2)
+ {
+ if (sub2)
+ FreeBrushList (sub2);
+ tail = AddBrushListToTail (sub, tail);
+ head = CullList (b1, b1);
+ goto newlist;
+ }
+ else
+ {
+ if (sub)
+ FreeBrushList (sub);
+ tail = AddBrushListToTail (sub2, tail);
+ head = CullList (b1, b2);
+ goto newlist;
+ }
+ }
+
+ if (!b2)
+ { // b1 is no longer intersecting anything, so keep it
+ b1->next = keep;
+ keep = b1;
+ }
+ }
+
+ qprintf ("output brushes: %i\n", CountBrushList (keep));
+#if DEBUG_BRUSHMODEL
+ if ( entity_num == DEBUG_BRUSHMODEL )
+ {
+ WriteBrushList ("after.gl", keep, false);
+ WriteBrushMap ("after.map", keep);
+ }
+#endif
+ return keep;
+}
+
+
diff --git a/mp/src/utils/vbsp/csg.h b/mp/src/utils/vbsp/csg.h new file mode 100644 index 00000000..30436dd9 --- /dev/null +++ b/mp/src/utils/vbsp/csg.h @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef CSG_H
+#define CSG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// Print a CONTENTS_ mask to a string.
+void PrintBrushContentsToString( int contents, char *pOut, int nMaxChars );
+
+// Print a CONTENTS_ mask with Msg().
+void PrintBrushContents( int contents );
+
+void FixupAreaportalWaterBrushes( bspbrush_t *pList );
+
+bspbrush_t *MakeBspBrushList (int startbrush, int endbrush,
+ const Vector& clipmins, const Vector& clipmaxs, int detailScreen);
+bspbrush_t *MakeBspBrushList (mapbrush_t **pBrushes, int nBrushCount, const Vector& clipmins, const Vector& clipmaxs);
+
+void WriteBrushMap (char *name, bspbrush_t *list);
+
+bspbrush_t *ChopBrushes (bspbrush_t *head);
+bspbrush_t *IntersectBrush (bspbrush_t *a, bspbrush_t *b);
+qboolean BrushesDisjoint (bspbrush_t *a, bspbrush_t *b);
+
+#endif // CSG_H
diff --git a/mp/src/utils/vbsp/cubemap.cpp b/mp/src/utils/vbsp/cubemap.cpp new file mode 100644 index 00000000..fda41703 --- /dev/null +++ b/mp/src/utils/vbsp/cubemap.cpp @@ -0,0 +1,995 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "bsplib.h"
+#include "tier1/UtlBuffer.h"
+#include "tier1/utlvector.h"
+#include "bitmap/imageformat.h"
+#include <KeyValues.h>
+#include "tier1/strtools.h"
+#include "tier1/utlsymbol.h"
+#include "vtf/vtf.h"
+#include "materialpatch.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+
+
+/*
+ Meager documentation for how the cubemaps are assigned.
+
+
+ While loading the map, it calls:
+ *** Cubemap_SaveBrushSides
+ Builds a list of what cubemaps manually were assigned to what faces
+ in s_EnvCubemapToBrushSides.
+
+ Immediately after loading the map, it calls:
+ *** Cubemap_FixupBrushSidesMaterials
+ Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
+ side referenced by an env_cubemap manually.
+
+ Then it calls Cubemap_AttachDefaultCubemapToSpecularSides:
+ *** Cubemap_InitCubemapSideData:
+ Setup s_aCubemapSideData.bHasEnvMapInMaterial and bManuallyPickedByAnEnvCubemap for each side.
+ bHasEnvMapInMaterial is set if the side's material has $envmap.
+ bManuallyPickedByAnEnvCubemap is true if the side was in s_EnvCubemapToBrushSides.
+
+ Then, for each bHasEnvMapInMaterial and !bManuallyPickedByAnEnvCubemap (ie: every specular surface that wasn't
+ referenced by some env_cubemap), it does Cubemap_CreateTexInfo.
+*/
+
+struct PatchInfo_t
+{
+ char *m_pMapName;
+ int m_pOrigin[3];
+};
+
+struct CubemapInfo_t
+{
+ int m_nTableId;
+ bool m_bSpecular;
+};
+
+static bool CubemapLessFunc( const CubemapInfo_t &lhs, const CubemapInfo_t &rhs )
+{
+ return ( lhs.m_nTableId < rhs.m_nTableId );
+}
+
+
+typedef CUtlVector<int> IntVector_t;
+static CUtlVector<IntVector_t> s_EnvCubemapToBrushSides;
+
+static CUtlVector<char *> s_DefaultCubemapNames;
+static char g_IsCubemapTexData[MAX_MAP_TEXDATA];
+
+
+struct CubemapSideData_t
+{
+ bool bHasEnvMapInMaterial;
+ bool bManuallyPickedByAnEnvCubemap;
+};
+
+static CubemapSideData_t s_aCubemapSideData[MAX_MAP_BRUSHSIDES];
+
+
+
+inline bool SideHasCubemapAndWasntManuallyReferenced( int iSide )
+{
+ return s_aCubemapSideData[iSide].bHasEnvMapInMaterial && !s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap;
+}
+
+
+void Cubemap_InsertSample( const Vector& origin, int size )
+{
+ dcubemapsample_t *pSample = &g_CubemapSamples[g_nCubemapSamples];
+ pSample->origin[0] = ( int )origin[0];
+ pSample->origin[1] = ( int )origin[1];
+ pSample->origin[2] = ( int )origin[2];
+ pSample->size = size;
+ g_nCubemapSamples++;
+}
+
+static const char *FindSkyboxMaterialName( void )
+{
+ for( int i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ char* pEntity = ValueForKey(&g_MainMap->entities[i], "classname");
+ if (!strcmp(pEntity, "worldspawn"))
+ {
+ return ValueForKey( &g_MainMap->entities[i], "skyname" );
+ }
+ }
+ return NULL;
+}
+
+static void BackSlashToForwardSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '\\' )
+ *pname = '/';
+ pname++;
+ }
+}
+
+static void ForwardSlashToBackSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '/' )
+ *pname = '\\';
+ pname++;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds materials that are used by a particular material
+//-----------------------------------------------------------------------------
+#define MAX_MATERIAL_NAME 512
+
+// This is the list of materialvars which are used in our codebase to look up dependent materials
+static const char *s_pDependentMaterialVar[] =
+{
+ "$bottommaterial", // Used by water materials
+ "$crackmaterial", // Used by shattered glass materials
+ "$fallbackmaterial", // Used by all materials
+
+ "", // Always must be last
+};
+
+static const char *FindDependentMaterial( const char *pMaterialName, const char **ppMaterialVar = NULL )
+{
+ // FIXME: This is a terrible way of doing this! It creates a dependency
+ // between vbsp and *all* code which reads dependent materials from materialvars
+ // At the time of writing this function, that means the engine + studiorender.
+ // We need a better way of figuring out how to do this, but for now I'm trying to do
+ // the fastest solution possible since it's close to ship
+
+ static char pDependentMaterialName[MAX_MATERIAL_NAME];
+ for( int i = 0; s_pDependentMaterialVar[i][0]; ++i )
+ {
+ if ( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName, MAX_MATERIAL_NAME - 1 ) )
+ continue;
+
+ if ( !Q_stricmp( pDependentMaterialName, pMaterialName ) )
+ {
+ Warning( "Material %s is depending on itself through materialvar %s! Ignoring...\n", pMaterialName, s_pDependentMaterialVar[i] );
+ continue;
+ }
+
+ // Return the material var that caused the dependency
+ if ( ppMaterialVar )
+ {
+ *ppMaterialVar = s_pDependentMaterialVar[i];
+ }
+
+#ifdef _DEBUG
+ // FIXME: Note that this code breaks if a material has more than 1 dependent material
+ ++i;
+ static char pDependentMaterialName2[MAX_MATERIAL_NAME];
+ while( s_pDependentMaterialVar[i][0] )
+ {
+ Assert( !GetValueFromMaterial( pMaterialName, s_pDependentMaterialVar[i], pDependentMaterialName2, MAX_MATERIAL_NAME - 1 ) );
+ ++i;
+ }
+#endif
+
+ return pDependentMaterialName;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads VTF files
+//-----------------------------------------------------------------------------
+static bool LoadSrcVTFFiles( IVTFTexture *pSrcVTFTextures[6], const char *pSkyboxMaterialBaseName,
+ int *pUnionTextureFlags, bool bHDR )
+{
+ const char *facingName[6] = { "rt", "lf", "bk", "ft", "up", "dn" };
+ int i;
+ for( i = 0; i < 6; i++ )
+ {
+ char srcMaterialName[1024];
+ sprintf( srcMaterialName, "%s%s", pSkyboxMaterialBaseName, facingName[i] );
+
+ IMaterial *pSkyboxMaterial = g_pMaterialSystem->FindMaterial( srcMaterialName, "skybox" );
+ //IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( bHDR ? "$hdrbasetexture" : "$basetexture", NULL ); //, bHDR ? false : true );
+ IMaterialVar *pSkyTextureVar = pSkyboxMaterial->FindVar( "$basetexture", NULL ); // Since we're setting it to black anyway, just use $basetexture for HDR
+ const char *vtfName = pSkyTextureVar->GetStringValue();
+ char srcVTFFileName[MAX_PATH];
+ Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
+
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
+ {
+ // Try looking for a compressed HDR texture
+ if ( bHDR )
+ {
+ /* // FIXME: We need a way to uncompress this format!
+ bool bHDRCompressed = true;
+
+ pSkyTextureVar = pSkyboxMaterial->FindVar( "$hdrcompressedTexture", NULL );
+ vtfName = pSkyTextureVar->GetStringValue();
+ Q_snprintf( srcVTFFileName, MAX_PATH, "materials/%s.vtf", vtfName );
+
+ if ( !g_pFullFileSystem->ReadFile( srcVTFFileName, NULL, buf ) )
+ */
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ pSrcVTFTextures[i] = CreateVTFTexture();
+ if (!pSrcVTFTextures[i]->Unserialize(buf))
+ {
+ Warning("*** Error unserializing skybox texture: %s\n", pSkyboxMaterialBaseName );
+ return false;
+ }
+
+ *pUnionTextureFlags |= pSrcVTFTextures[i]->Flags();
+ int flagsNoAlpha = pSrcVTFTextures[i]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
+ int flagsFirstNoAlpha = pSrcVTFTextures[0]->Flags() & ~( TEXTUREFLAGS_EIGHTBITALPHA | TEXTUREFLAGS_ONEBITALPHA );
+
+ // NOTE: texture[0] is a side texture that could be 1/2 height, so allow this and also allow 4x4 faces
+ if ( ( ( pSrcVTFTextures[i]->Width() != pSrcVTFTextures[0]->Width() ) && ( pSrcVTFTextures[i]->Width() != 4 ) ) ||
+ ( ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height() ) && ( pSrcVTFTextures[i]->Height() != pSrcVTFTextures[0]->Height()*2 ) && ( pSrcVTFTextures[i]->Height() != 4 ) ) ||
+ ( flagsNoAlpha != flagsFirstNoAlpha ) )
+ {
+ Warning("*** Error: Skybox vtf files for %s weren't compiled with the same size texture and/or same flags!\n", pSkyboxMaterialBaseName );
+ return false;
+ }
+
+ if ( bHDR )
+ {
+ pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGB323232F, false );
+ pSrcVTFTextures[i]->GenerateMipmaps();
+ pSrcVTFTextures[i]->ConvertImageFormat( IMAGE_FORMAT_RGBA16161616F, false );
+ }
+ }
+
+ return true;
+}
+
+void VTFNameToHDRVTFName( const char *pSrcName, char *pDest, int maxLen, bool bHDR )
+{
+ Q_strncpy( pDest, pSrcName, maxLen );
+ if( !bHDR )
+ {
+ return;
+ }
+ char *pDot = Q_stristr( pDest, ".vtf" );
+ if( !pDot )
+ {
+ return;
+ }
+ Q_strncpy( pDot, ".hdr.vtf", maxLen - ( pDot - pDest ) );
+}
+
+#define DEFAULT_CUBEMAP_SIZE 32
+
+void CreateDefaultCubemaps( bool bHDR )
+{
+ memset( g_IsCubemapTexData, 0, sizeof(g_IsCubemapTexData) );
+
+ // NOTE: This implementation depends on the fact that all VTF files contain
+ // all mipmap levels
+ const char *pSkyboxBaseName = FindSkyboxMaterialName();
+ char skyboxMaterialName[MAX_PATH];
+ Q_snprintf( skyboxMaterialName, MAX_PATH, "skybox/%s", pSkyboxBaseName );
+
+ IVTFTexture *pSrcVTFTextures[6];
+
+ if( !skyboxMaterialName )
+ {
+ if( s_DefaultCubemapNames.Count() )
+ {
+ Warning( "This map uses env_cubemap, and you don't have a skybox, so no default env_cubemaps will be generated.\n" );
+ }
+ return;
+ }
+
+ int unionTextureFlags = 0;
+ if( !LoadSrcVTFFiles( pSrcVTFTextures, skyboxMaterialName, &unionTextureFlags, bHDR ) )
+ {
+ Warning( "Can't load skybox file %s to build the default cubemap!\n", skyboxMaterialName );
+ return;
+ }
+ Msg( "Creating default %scubemaps for env_cubemap using skybox materials:\n %s*.vmt\n"
+ " ! Run buildcubemaps in the engine to get the correct cube maps.\n", bHDR ? "HDR " : "LDR ", skyboxMaterialName );
+
+ // Figure out the mip differences between the two textures
+ int iMipLevelOffset = 0;
+ int tmp = pSrcVTFTextures[0]->Width();
+ while( tmp > DEFAULT_CUBEMAP_SIZE )
+ {
+ iMipLevelOffset++;
+ tmp >>= 1;
+ }
+
+ // Create the destination cubemap
+ IVTFTexture *pDstCubemap = CreateVTFTexture();
+ pDstCubemap->Init( DEFAULT_CUBEMAP_SIZE, DEFAULT_CUBEMAP_SIZE, 1,
+ pSrcVTFTextures[0]->Format(), unionTextureFlags | TEXTUREFLAGS_ENVMAP,
+ pSrcVTFTextures[0]->FrameCount() );
+
+ // First iterate over all frames
+ for (int iFrame = 0; iFrame < pDstCubemap->FrameCount(); ++iFrame)
+ {
+ // Next iterate over all normal cube faces (we know there's 6 cause it's an envmap)
+ for (int iFace = 0; iFace < 6; ++iFace )
+ {
+ // Finally, iterate over all mip levels in the *destination*
+ for (int iMip = 0; iMip < pDstCubemap->MipCount(); ++iMip )
+ {
+ // Copy the bits from the source images into the cube faces
+ unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, iMip + iMipLevelOffset );
+ unsigned char *pDstBits = pDstCubemap->ImageData( iFrame, iFace, iMip );
+ int iSize = pDstCubemap->ComputeMipSize( iMip );
+ int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( iMip + iMipLevelOffset );
+
+ // !!! FIXME: Set this to black until HDR cubemaps are built properly!
+ memset( pDstBits, 0, iSize );
+ continue;
+
+ if ( ( pSrcVTFTextures[iFace]->Width() == 4 ) && ( pSrcVTFTextures[iFace]->Height() == 4 ) ) // If texture is 4x4 square
+ {
+ // Force mip level 2 to get the 1x1 face
+ unsigned char *pSrcBits = pSrcVTFTextures[iFace]->ImageData( iFrame, 0, 2 );
+ int iSrcMipSize = pSrcVTFTextures[iFace]->ComputeMipSize( 2 );
+
+ // Replicate 1x1 mip level across entire face
+ //memset( pDstBits, 0, iSize );
+ for ( int i = 0; i < ( iSize / iSrcMipSize ); i++ )
+ {
+ memcpy( pDstBits + ( i * iSrcMipSize ), pSrcBits, iSrcMipSize );
+ }
+ }
+ else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height() ) // If texture is square
+ {
+ if ( iSrcMipSize != iSize )
+ {
+ Warning( "%s - ERROR! Cannot copy square face for default cubemap! iSrcMipSize(%d) != iSize(%d)\n", skyboxMaterialName, iSrcMipSize, iSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ // Just copy the mip level
+ memcpy( pDstBits, pSrcBits, iSize );
+ }
+ }
+ else if ( pSrcVTFTextures[iFace]->Width() == pSrcVTFTextures[iFace]->Height()*2 ) // If texture is rectangle 2x wide
+ {
+ int iMipWidth, iMipHeight, iMipDepth;
+ pDstCubemap->ComputeMipLevelDimensions( iMip, &iMipWidth, &iMipHeight, &iMipDepth );
+ if ( ( iMipHeight > 1 ) && ( iSrcMipSize*2 != iSize ) )
+ {
+ Warning( "%s - ERROR building default cube map! %d*2 != %d\n", skyboxMaterialName, iSrcMipSize, iSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ // Copy row at a time and repeat last row
+ memcpy( pDstBits, pSrcBits, iSize/2 );
+ //memcpy( pDstBits + iSize/2, pSrcBits, iSize/2 );
+ int nSrcRowSize = pSrcVTFTextures[iFace]->RowSizeInBytes( iMip + iMipLevelOffset );
+ int nDstRowSize = pDstCubemap->RowSizeInBytes( iMip );
+ if ( nSrcRowSize != nDstRowSize )
+ {
+ Warning( "%s - ERROR building default cube map! nSrcRowSize(%d) != nDstRowSize(%d)!\n", skyboxMaterialName, nSrcRowSize, nDstRowSize );
+ memset( pDstBits, 0, iSize );
+ }
+ else
+ {
+ for ( int i = 0; i < ( iSize/2 / nSrcRowSize ); i++ )
+ {
+ memcpy( pDstBits + iSize/2 + i*nSrcRowSize, pSrcBits + iSrcMipSize - nSrcRowSize, nSrcRowSize );
+ }
+ }
+ }
+ }
+ else
+ {
+ // ERROR! This code only supports square and rectangluar 2x wide
+ Warning( "%s - Couldn't create default cubemap because texture res is %dx%d\n", skyboxMaterialName, pSrcVTFTextures[iFace]->Width(), pSrcVTFTextures[iFace]->Height() );
+ memset( pDstBits, 0, iSize );
+ return;
+ }
+ }
+ }
+ }
+
+ ImageFormat originalFormat = pDstCubemap->Format();
+ if( !bHDR )
+ {
+ // Convert the cube to format that we can apply tools to it...
+ pDstCubemap->ConvertImageFormat( IMAGE_FORMAT_DEFAULT, false );
+ }
+
+ // Fixup the cubemap facing
+ pDstCubemap->FixCubemapFaceOrientation();
+
+ // Now that the bits are in place, compute the spheremaps...
+ pDstCubemap->GenerateSpheremap();
+
+ if( !bHDR )
+ {
+ // Convert the cubemap to the final format
+ pDstCubemap->ConvertImageFormat( originalFormat, false );
+ }
+
+ // Write the puppy out!
+ char dstVTFFileName[1024];
+ if( bHDR )
+ {
+ sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.hdr.vtf", mapbase );
+ }
+ else
+ {
+ sprintf( dstVTFFileName, "materials/maps/%s/cubemapdefault.vtf", mapbase );
+ }
+
+ CUtlBuffer outputBuf;
+ if (!pDstCubemap->Serialize( outputBuf ))
+ {
+ Warning( "Error serializing default cubemap %s\n", dstVTFFileName );
+ return;
+ }
+
+ IZip *pak = GetPakFile();
+
+ // spit out the default one.
+ AddBufferToPak( pak, dstVTFFileName, outputBuf.Base(), outputBuf.TellPut(), false );
+
+ // spit out all of the ones that are attached to world geometry.
+ int i;
+ for( i = 0; i < s_DefaultCubemapNames.Count(); i++ )
+ {
+ char vtfName[MAX_PATH];
+ VTFNameToHDRVTFName( s_DefaultCubemapNames[i], vtfName, MAX_PATH, bHDR );
+ if( FileExistsInPak( pak, vtfName ) )
+ {
+ continue;
+ }
+ AddBufferToPak( pak, vtfName, outputBuf.Base(),outputBuf.TellPut(), false );
+ }
+
+ // Clean up the textures
+ for( i = 0; i < 6; i++ )
+ {
+ DestroyVTFTexture( pSrcVTFTextures[i] );
+ }
+ DestroyVTFTexture( pDstCubemap );
+}
+
+void Cubemap_CreateDefaultCubemaps( void )
+{
+ CreateDefaultCubemaps( false );
+ CreateDefaultCubemaps( true );
+}
+
+// Builds a list of what cubemaps manually were assigned to what faces
+// in s_EnvCubemapToBrushSides.
+void Cubemap_SaveBrushSides( const char *pSideListStr )
+{
+ IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[s_EnvCubemapToBrushSides.AddToTail()];
+ char *pTmp = ( char * )_alloca( strlen( pSideListStr ) + 1 );
+ strcpy( pTmp, pSideListStr );
+ const char *pScan = strtok( pTmp, " " );
+ if( !pScan )
+ {
+ return;
+ }
+ do
+ {
+ int brushSideID;
+ if( sscanf( pScan, "%d", &brushSideID ) == 1 )
+ {
+ brushSidesVector.AddToTail( brushSideID );
+ }
+ } while( ( pScan = strtok( NULL, " " ) ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Generate patched material name
+//-----------------------------------------------------------------------------
+static void GeneratePatchedName( const char *pMaterialName, const PatchInfo_t &info, bool bMaterialName, char *pBuffer, int nMaxLen )
+{
+ const char *pSeparator = bMaterialName ? "_" : "";
+ int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s%s%d_%d_%d", info.m_pMapName,
+ pMaterialName, pSeparator, info.m_pOrigin[0], info.m_pOrigin[1], info.m_pOrigin[2] );
+
+ if ( bMaterialName )
+ {
+ Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
+ if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
+ {
+ Error( "Generated env_cubemap patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
+ }
+ }
+
+ BackSlashToForwardSlash( pBuffer );
+ Q_strlower( pBuffer );
+}
+
+
+//-----------------------------------------------------------------------------
+// Patches the $envmap for a material and all its dependents, returns true if any patching happened
+//-----------------------------------------------------------------------------
+static bool PatchEnvmapForMaterialAndDependents( const char *pMaterialName, const PatchInfo_t &info, const char *pCubemapTexture )
+{
+ // Do *NOT* patch the material if there is an $envmap specified and it's not 'env_cubemap'
+
+ // FIXME: It's theoretically ok to patch the material if $envmap is not specified,
+ // because we're using the 'replace' block, which will only add the env_cubemap if
+ // $envmap is specified in the source material. But it will fail if someone adds
+ // a specific non-env_cubemap $envmap to the source material at a later point. Bleah
+
+ // See if we have an $envmap to patch
+ bool bShouldPatchEnvCubemap = DoesMaterialHaveKeyValuePair( pMaterialName, "$envmap", "env_cubemap" );
+
+ // See if we have a dependent material to patch
+ bool bDependentMaterialPatched = false;
+ const char *pDependentMaterialVar = NULL;
+ const char *pDependentMaterial = FindDependentMaterial( pMaterialName, &pDependentMaterialVar );
+ if ( pDependentMaterial )
+ {
+ bDependentMaterialPatched = PatchEnvmapForMaterialAndDependents( pDependentMaterial, info, pCubemapTexture );
+ }
+
+ // If we have neither to patch, we're done
+ if ( !bShouldPatchEnvCubemap && !bDependentMaterialPatched )
+ return false;
+
+ // Otherwise we have to make a patched version of ourselves
+ char pPatchedMaterialName[1024];
+ GeneratePatchedName( pMaterialName, info, true, pPatchedMaterialName, 1024 );
+
+ MaterialPatchInfo_t pPatchInfo[2];
+ int nPatchCount = 0;
+ if ( bShouldPatchEnvCubemap )
+ {
+ pPatchInfo[nPatchCount].m_pKey = "$envmap";
+ pPatchInfo[nPatchCount].m_pRequiredOriginalValue = "env_cubemap";
+ pPatchInfo[nPatchCount].m_pValue = pCubemapTexture;
+ ++nPatchCount;
+ }
+
+ char pDependentPatchedMaterialName[1024];
+ if ( bDependentMaterialPatched )
+ {
+ // FIXME: Annoying! I either have to pass back the patched dependent material name
+ // or reconstruct it. Both are sucky.
+ GeneratePatchedName( pDependentMaterial, info, true, pDependentPatchedMaterialName, 1024 );
+ pPatchInfo[nPatchCount].m_pKey = pDependentMaterialVar;
+ pPatchInfo[nPatchCount].m_pValue = pDependentPatchedMaterialName;
+ ++nPatchCount;
+ }
+
+ CreateMaterialPatch( pMaterialName, pPatchedMaterialName, nPatchCount, pPatchInfo, PATCH_REPLACE );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a texinfo that has a particular
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Create a VMT to override the specified texinfo which references the cubemap entity at the specified origin.
+// Returns the index of the new (or preexisting) texinfo referencing that VMT.
+//
+// Also adds the new cubemap VTF filename to s_DefaultCubemapNames so it can copy the
+// default (skybox) cubemap into this file so the cubemap doesn't have the pink checkerboard at
+// runtime before they run buildcubemaps.
+//-----------------------------------------------------------------------------
+static int Cubemap_CreateTexInfo( int originalTexInfo, int origin[3] )
+{
+ // Don't make cubemap tex infos for nodes
+ if ( originalTexInfo == TEXINFO_NODE )
+ return originalTexInfo;
+
+ texinfo_t *pTexInfo = &texinfo[originalTexInfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ if ( g_IsCubemapTexData[pTexInfo->texdata] )
+ {
+ Warning("Multiple references for cubemap on texture %s!!!\n", pMaterialName );
+ return originalTexInfo;
+ }
+
+ // Get out of here if the originalTexInfo is already a generated material for this position.
+ char pStringToSearchFor[512];
+ Q_snprintf( pStringToSearchFor, 512, "_%d_%d_%d", origin[0], origin[1], origin[2] );
+ if ( Q_stristr( pMaterialName, pStringToSearchFor ) )
+ return originalTexInfo;
+
+ // Package up information needed to generate patch names
+ PatchInfo_t info;
+ info.m_pMapName = mapbase;
+ info.m_pOrigin[0] = origin[0];
+ info.m_pOrigin[1] = origin[1];
+ info.m_pOrigin[2] = origin[2];
+
+ // Generate the name of the patched material
+ char pGeneratedTexDataName[1024];
+ GeneratePatchedName( pMaterialName, info, true, pGeneratedTexDataName, 1024 );
+
+ // Make sure the texdata doesn't already exist.
+ int nTexDataID = FindTexData( pGeneratedTexDataName );
+ bool bHasTexData = (nTexDataID != -1);
+ if( !bHasTexData )
+ {
+ // Generate the new "$envmap" texture name.
+ char pTextureName[1024];
+ GeneratePatchedName( "c", info, false, pTextureName, 1024 );
+
+ // Hook the texture into the material and all dependent materials
+ // but if no hooking was necessary, exit out
+ if ( !PatchEnvmapForMaterialAndDependents( pMaterialName, info, pTextureName ) )
+ return originalTexInfo;
+
+ // Store off the name of the cubemap that we need to create since we successfully patched
+ char pFileName[1024];
+ int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
+ int id = s_DefaultCubemapNames.AddToTail();
+ s_DefaultCubemapNames[id] = new char[ nLen + 1 ];
+ strcpy( s_DefaultCubemapNames[id], pFileName );
+
+ // Make a new texdata
+ nTexDataID = AddCloneTexData( pTexData, pGeneratedTexDataName );
+ g_IsCubemapTexData[nTexDataID] = true;
+ }
+
+ Assert( nTexDataID != -1 );
+
+ texinfo_t newTexInfo;
+ newTexInfo = *pTexInfo;
+ newTexInfo.texdata = nTexDataID;
+
+ int nTexInfoID = -1;
+
+ // See if we need to make a new texinfo
+ bool bHasTexInfo = false;
+ if( bHasTexData )
+ {
+ nTexInfoID = FindTexInfo( newTexInfo );
+ bHasTexInfo = (nTexInfoID != -1);
+ }
+
+ // Make a new texinfo if we need to.
+ if( !bHasTexInfo )
+ {
+ nTexInfoID = texinfo.AddToTail( newTexInfo );
+ }
+
+ Assert( nTexInfoID != -1 );
+ return nTexInfoID;
+}
+
+static int SideIDToIndex( int brushSideID )
+{
+ int i;
+ for( i = 0; i < g_MainMap->nummapbrushsides; i++ )
+ {
+ if( g_MainMap->brushsides[i].id == brushSideID )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Goes through s_EnvCubemapToBrushSides and does Cubemap_CreateTexInfo for each
+// side referenced by an env_cubemap manually.
+//-----------------------------------------------------------------------------
+void Cubemap_FixupBrushSidesMaterials( void )
+{
+ Msg( "fixing up env_cubemap materials on brush sides...\n" );
+ Assert( s_EnvCubemapToBrushSides.Count() == g_nCubemapSamples );
+
+ int cubemapID;
+ for( cubemapID = 0; cubemapID < g_nCubemapSamples; cubemapID++ )
+ {
+ IntVector_t &brushSidesVector = s_EnvCubemapToBrushSides[cubemapID];
+ int i;
+ for( i = 0; i < brushSidesVector.Count(); i++ )
+ {
+ int brushSideID = brushSidesVector[i];
+ int sideIndex = SideIDToIndex( brushSideID );
+ if( sideIndex < 0 )
+ {
+ Warning("env_cubemap pointing at deleted brushside near (%d, %d, %d)\n",
+ g_CubemapSamples[cubemapID].origin[0], g_CubemapSamples[cubemapID].origin[1], g_CubemapSamples[cubemapID].origin[2] );
+
+ continue;
+ }
+
+ side_t *pSide = &g_MainMap->brushsides[sideIndex];
+
+#ifdef DEBUG
+ if ( pSide->pMapDisp )
+ {
+ Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
+ }
+#endif
+
+ pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[cubemapID].origin );
+ if ( pSide->pMapDisp )
+ {
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Cubemap_ResetCubemapSideData( void )
+{
+ for ( int iSide = 0; iSide < MAX_MAP_BRUSHSIDES; ++iSide )
+ {
+ s_aCubemapSideData[iSide].bHasEnvMapInMaterial = false;
+ s_aCubemapSideData[iSide].bManuallyPickedByAnEnvCubemap = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the material or any of its dependents use an $envmap
+//-----------------------------------------------------------------------------
+bool DoesMaterialOrDependentsUseEnvmap( const char *pPatchedMaterialName )
+{
+ const char *pOriginalMaterialName = GetOriginalMaterialNameForPatchedMaterial( pPatchedMaterialName );
+ if( DoesMaterialHaveKey( pOriginalMaterialName, "$envmap" ) )
+ return true;
+
+ const char *pDependentMaterial = FindDependentMaterial( pOriginalMaterialName );
+ if ( !pDependentMaterial )
+ return false;
+
+ return DoesMaterialOrDependentsUseEnvmap( pDependentMaterial );
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of all texdatas which need fixing up
+//-----------------------------------------------------------------------------
+void Cubemap_InitCubemapSideData( void )
+{
+ // This tree is used to prevent re-parsing material vars multiple times
+ CUtlRBTree<CubemapInfo_t> lookup( 0, g_MainMap->nummapbrushsides, CubemapLessFunc );
+
+ // Fill in specular data.
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+ if ( !pSide )
+ continue;
+
+ if ( pSide->texinfo == TEXINFO_NODE )
+ continue;
+
+ texinfo_t *pTex = &texinfo[pSide->texinfo];
+ if ( !pTex )
+ continue;
+
+ dtexdata_t *pTexData = GetTexData( pTex->texdata );
+ if ( !pTexData )
+ continue;
+
+ CubemapInfo_t info;
+ info.m_nTableId = pTexData->nameStringTableID;
+
+ // Have we encountered this materal? If so, then copy the data we cached off before
+ int i = lookup.Find( info );
+ if ( i != lookup.InvalidIndex() )
+ {
+ s_aCubemapSideData[iSide].bHasEnvMapInMaterial = lookup[i].m_bSpecular;
+ continue;
+ }
+
+ // First time we've seen this material. Figure out if it uses env_cubemap
+ const char *pPatchedMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ info.m_bSpecular = DoesMaterialOrDependentsUseEnvmap( pPatchedMaterialName );
+ s_aCubemapSideData[ iSide ].bHasEnvMapInMaterial = info.m_bSpecular;
+ lookup.Insert( info );
+ }
+
+ // Fill in cube map data.
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ IntVector_t &sideList = s_EnvCubemapToBrushSides[iCubemap];
+ int nSideCount = sideList.Count();
+ for ( int iSide = 0; iSide < nSideCount; ++iSide )
+ {
+ int nSideID = sideList[iSide];
+ int nIndex = SideIDToIndex( nSideID );
+ if ( nIndex < 0 )
+ continue;
+
+ s_aCubemapSideData[nIndex].bManuallyPickedByAnEnvCubemap = true;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int Cubemap_FindClosestCubemap( const Vector &entityOrigin, side_t *pSide )
+{
+ if ( !pSide )
+ return -1;
+
+ // Return a valid (if random) cubemap if there's no winding
+ if ( !pSide->winding )
+ return 0;
+
+ // Calculate the center point.
+ Vector vecCenter;
+ vecCenter.Init();
+
+ for ( int iPoint = 0; iPoint < pSide->winding->numpoints; ++iPoint )
+ {
+ VectorAdd( vecCenter, pSide->winding->p[iPoint], vecCenter );
+ }
+ VectorScale( vecCenter, 1.0f / pSide->winding->numpoints, vecCenter );
+ vecCenter += entityOrigin;
+ plane_t *pPlane = &g_MainMap->mapplanes[pSide->planenum];
+
+ // Find the closest cubemap.
+ int iMinCubemap = -1;
+ float flMinDist = FLT_MAX;
+
+ // Look for cubemaps in front of the surface first.
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
+ Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
+ static_cast<float>( pSample->origin[1] ),
+ static_cast<float>( pSample->origin[2] ) );
+ Vector vecDelta;
+ VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
+ float flDist = vecDelta.NormalizeInPlace();
+ float flDot = DotProduct( vecDelta, pPlane->normal );
+ if ( ( flDot >= 0.0f ) && ( flDist < flMinDist ) )
+ {
+ flMinDist = flDist;
+ iMinCubemap = iCubemap;
+ }
+ }
+
+ // Didn't find anything in front search for closest.
+ if( iMinCubemap == -1 )
+ {
+ for ( int iCubemap = 0; iCubemap < g_nCubemapSamples; ++iCubemap )
+ {
+ dcubemapsample_t *pSample = &g_CubemapSamples[iCubemap];
+ Vector vecSampleOrigin( static_cast<float>( pSample->origin[0] ),
+ static_cast<float>( pSample->origin[1] ),
+ static_cast<float>( pSample->origin[2] ) );
+ Vector vecDelta;
+ VectorSubtract( vecSampleOrigin, vecCenter, vecDelta );
+ float flDist = vecDelta.Length();
+ if ( flDist < flMinDist )
+ {
+ flMinDist = flDist;
+ iMinCubemap = iCubemap;
+ }
+ }
+ }
+
+ return iMinCubemap;
+}
+
+
+//-----------------------------------------------------------------------------
+// For every specular surface that wasn't referenced by some env_cubemap, call Cubemap_CreateTexInfo.
+//-----------------------------------------------------------------------------
+void Cubemap_AttachDefaultCubemapToSpecularSides( void )
+{
+ Cubemap_ResetCubemapSideData();
+ Cubemap_InitCubemapSideData();
+
+ // build a mapping from side to entity id so that we can get the entity origin
+ CUtlVector<int> sideToEntityIndex;
+ sideToEntityIndex.SetCount(g_MainMap->nummapbrushsides);
+ int i;
+ for ( i = 0; i < g_MainMap->nummapbrushsides; i++ )
+ {
+ sideToEntityIndex[i] = -1;
+ }
+
+ for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
+ {
+ int entityIndex = g_MainMap->mapbrushes[i].entitynum;
+ for ( int j = 0; j < g_MainMap->mapbrushes[i].numsides; j++ )
+ {
+ side_t *side = &g_MainMap->mapbrushes[i].original_sides[j];
+ int sideIndex = side - g_MainMap->brushsides;
+ sideToEntityIndex[sideIndex] = entityIndex;
+ }
+ }
+
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+ if ( !SideHasCubemapAndWasntManuallyReferenced( iSide ) )
+ continue;
+
+
+ int currentEntity = sideToEntityIndex[iSide];
+
+ int iCubemap = Cubemap_FindClosestCubemap( g_MainMap->entities[currentEntity].origin, pSide );
+ if ( iCubemap == -1 )
+ continue;
+
+#ifdef DEBUG
+ if ( pSide->pMapDisp )
+ {
+ Assert( pSide->texinfo == pSide->pMapDisp->face.texinfo );
+ }
+#endif
+ pSide->texinfo = Cubemap_CreateTexInfo( pSide->texinfo, g_CubemapSamples[iCubemap].origin );
+ if ( pSide->pMapDisp )
+ {
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ }
+ }
+}
+
+// Populate with cubemaps that were skipped
+void Cubemap_AddUnreferencedCubemaps()
+{
+ char pTextureName[1024];
+ char pFileName[1024];
+ PatchInfo_t info;
+ dcubemapsample_t *pSample;
+ int i,j;
+
+ for ( i=0; i<g_nCubemapSamples; ++i )
+ {
+ pSample = &g_CubemapSamples[i];
+
+ // generate the formatted texture name based on cubemap origin
+ info.m_pMapName = mapbase;
+ info.m_pOrigin[0] = pSample->origin[0];
+ info.m_pOrigin[1] = pSample->origin[1];
+ info.m_pOrigin[2] = pSample->origin[2];
+ GeneratePatchedName( "c", info, false, pTextureName, 1024 );
+
+ // find or add
+ for ( j=0; j<s_DefaultCubemapNames.Count(); ++j )
+ {
+ if ( !stricmp( s_DefaultCubemapNames[j], pTextureName ) )
+ {
+ // already added
+ break;
+ }
+ }
+ if ( j == s_DefaultCubemapNames.Count() )
+ {
+ int nLen = Q_snprintf( pFileName, 1024, "materials/%s.vtf", pTextureName );
+
+ int id = s_DefaultCubemapNames.AddToTail();
+ s_DefaultCubemapNames[id] = new char[nLen + 1];
+ strcpy( s_DefaultCubemapNames[id], pFileName );
+ }
+ }
+}
diff --git a/mp/src/utils/vbsp/detail.cpp b/mp/src/utils/vbsp/detail.cpp new file mode 100644 index 00000000..88071dd3 --- /dev/null +++ b/mp/src/utils/vbsp/detail.cpp @@ -0,0 +1,693 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Builds/merges the BSP tree of detail brushes
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "detail.h"
+#include "utlvector.h"
+#include <assert.h>
+
+face_t *NewFaceFromFace (face_t *f);
+face_t *ComputeVisibleBrushSides( bspbrush_t *list );
+
+//-----------------------------------------------------------------------------
+// Purpose: Copies a face and its winding
+// Input : *pFace -
+// Output : face_t
+//-----------------------------------------------------------------------------
+face_t *CopyFace( face_t *pFace )
+{
+ face_t *f = NewFaceFromFace( pFace );
+ f->w = CopyWinding( pFace->w );
+
+ return f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Link this brush into the list for this leaf
+// Input : *node -
+// *brush -
+//-----------------------------------------------------------------------------
+void AddBrushToLeaf( node_t *node, bspbrush_t *brush )
+{
+ brush->next = node->brushlist;
+ node->brushlist = brush;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recursively filter a brush through the tree
+// Input : *node -
+// *brush -
+//-----------------------------------------------------------------------------
+void MergeBrush_r( node_t *node, bspbrush_t *brush )
+{
+ if ( node->planenum == PLANENUM_LEAF )
+ {
+ if ( node->contents & CONTENTS_SOLID )
+ {
+ FreeBrush( brush );
+ }
+ else
+ {
+ AddBrushToLeaf( node, brush );
+ }
+ return;
+ }
+
+ bspbrush_t *front, *back;
+ SplitBrush( brush, node->planenum, &front, &back );
+ FreeBrush( brush );
+
+ if ( front )
+ {
+ MergeBrush_r( node->children[0], front );
+ }
+ if ( back )
+ {
+ MergeBrush_r( node->children[1], back );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Recursively filter a face into the tree leaving references to the
+// original face in any visible leaves that a clipped fragment falls
+// into.
+// Input : *node - current head of tree
+// *face - clipped face fragment
+// *original - unclipped original face
+// Output : Returns true if any references were left
+//-----------------------------------------------------------------------------
+bool MergeFace_r( node_t *node, face_t *face, face_t *original )
+{
+ bool referenced = false;
+
+ if ( node->planenum == PLANENUM_LEAF )
+ {
+ if ( node->contents & CONTENTS_SOLID )
+ {
+ FreeFace( face );
+ return false;
+ }
+
+ leafface_t *plist = new leafface_t;
+ plist->pFace = original;
+ plist->pNext = node->leaffacelist;
+ node->leaffacelist = plist;
+
+ referenced = true;
+ }
+ else
+ {
+ // UNDONE: Don't copy the faces each time unless it's necessary!?!?!
+ plane_t *plane = &g_MainMap->mapplanes[node->planenum];
+ winding_t *frontwinding, *backwinding, *onwinding;
+
+ Vector offset;
+ WindingCenter( face->w, offset );
+
+ // UNDONE: Export epsilon from original face clipping code
+ ClassifyWindingEpsilon_Offset(face->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, &onwinding, -offset);
+
+ if ( onwinding )
+ {
+ // face is in the split plane, go down the appropriate side according to the facing direction
+ assert( frontwinding == NULL );
+ assert( backwinding == NULL );
+
+ if ( DotProduct( g_MainMap->mapplanes[face->planenum].normal, g_MainMap->mapplanes[node->planenum].normal ) > 0 )
+ {
+ frontwinding = onwinding;
+ }
+ else
+ {
+ backwinding = onwinding;
+ }
+ }
+
+ if ( frontwinding )
+ {
+ face_t *tmp = NewFaceFromFace( face );
+ tmp->w = frontwinding;
+ referenced = MergeFace_r( node->children[0], tmp, original );
+ }
+ if ( backwinding )
+ {
+ face_t *tmp = NewFaceFromFace( face );
+ tmp->w = backwinding;
+ bool test = MergeFace_r( node->children[1], tmp, original );
+ referenced = referenced || test;
+ }
+ }
+ FreeFace( face );
+
+ return referenced;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Loop through each face and filter it into the tree
+// Input : *out -
+// *pFaces -
+//-----------------------------------------------------------------------------
+face_t *FilterFacesIntoTree( tree_t *out, face_t *pFaces )
+{
+ face_t *pLeafFaceList = NULL;
+ for ( face_t *f = pFaces; f; f = f->next )
+ {
+ if( f->merged || f->split[0] || f->split[1] )
+ continue;
+
+ face_t *tmp = CopyFace( f );
+ face_t *original = CopyFace( f );
+
+ if ( MergeFace_r( out->headnode, tmp, original ) )
+ {
+ // clear out portal (comes from a different tree)
+ original->portal = NULL;
+ original->next = pLeafFaceList;
+ pLeafFaceList = original;
+ }
+ else
+ {
+ FreeFace( original );
+ }
+ }
+
+ return pLeafFaceList;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Splits the face list into faces from the same plane and tries to merge
+// them if possible
+// Input : **pFaceList -
+//-----------------------------------------------------------------------------
+void TryMergeFaceList( face_t **pFaceList )
+{
+ face_t **pPlaneList = NULL;
+
+ // divide the list into buckets by plane number
+ pPlaneList = new face_t *[g_MainMap->nummapplanes];
+ memset( pPlaneList, 0, sizeof(face_t *) * g_MainMap->nummapplanes );
+
+ face_t *pFaces = *pFaceList;
+ face_t *pOutput = NULL;
+
+ while ( pFaces )
+ {
+ face_t *next = pFaces->next;
+
+ // go ahead and delete the old split/merged faces
+ if ( pFaces->merged || pFaces->split[0] || pFaces->split[1] )
+ {
+ Error("Split face in merge list!");
+ }
+ else
+ {
+ // add to the list for this plane
+ pFaces->next = pPlaneList[pFaces->planenum];
+ pPlaneList[pFaces->planenum] = pFaces;
+ }
+
+ pFaces = next;
+ }
+
+ // now merge each plane's list of faces
+ int merged = 0;
+ for ( int i = 0; i < g_MainMap->nummapplanes; i++ )
+ {
+ if ( pPlaneList[i] )
+ {
+ MergeFaceList( &pPlaneList[i] );
+ }
+
+ // move these over to the output face list
+ face_t *list = pPlaneList[i];
+ while ( list )
+ {
+ face_t *next = list->next;
+
+ if ( list->merged )
+ merged++;
+
+ list->next = pOutput;
+ pOutput = list;
+ list = next;
+ }
+ }
+
+ if ( merged )
+ {
+ Msg("\nMerged %d detail faces...", merged );
+ }
+ delete[] pPlaneList;
+
+ *pFaceList = pOutput;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: filter each brush in the list into the tree
+// Input : *out -
+// *brushes -
+//-----------------------------------------------------------------------------
+void FilterBrushesIntoTree( tree_t *out, bspbrush_t *brushes )
+{
+ // Merge all of the brushes into the world tree
+ for ( bspbrush_t *plist = brushes; plist; plist = plist->next )
+ {
+ MergeBrush_r( out->headnode, CopyBrush(plist) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Build faces for the detail brushes and merge them into the BSP
+// Input : *worldtree -
+// brush_start -
+// brush_end -
+//-----------------------------------------------------------------------------
+face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end )
+{
+ int start;
+ bspbrush_t *detailbrushes = NULL;
+ face_t *pFaces = NULL;
+ face_t *pLeafFaceList = NULL;
+
+ // Grab the list of detail brushes
+ detailbrushes = MakeBspBrushList (brush_start, brush_end, g_MainMap->map_mins, g_MainMap->map_maxs, ONLY_DETAIL );
+ if (detailbrushes)
+ {
+ start = Plat_FloatTime();
+ Msg("Chop Details...");
+ // if there are detail brushes, chop them against each other
+ if (!nocsg)
+ detailbrushes = ChopBrushes (detailbrushes);
+
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+ // Now mark the visible sides so we can eliminate all detail brush sides
+ // that are covered by other detail brush sides
+ // NOTE: This still leaves detail brush sides that are covered by the world. (these are removed in the merge operation)
+ Msg("Find Visible Detail Sides...");
+ pFaces = ComputeVisibleBrushSides( detailbrushes );
+ TryMergeFaceList( &pFaces );
+ SubdivideFaceList( &pFaces );
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+
+ start = Plat_FloatTime();
+ Msg("Merging details...");
+ // Merge the detail solids and faces into the world tree
+ // Merge all of the faces into the world tree
+ pLeafFaceList = FilterFacesIntoTree( worldtree, pFaces );
+ FilterBrushesIntoTree( worldtree, detailbrushes );
+
+ FreeFaceList( pFaces );
+ FreeBrushList(detailbrushes);
+
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+ }
+
+ return pLeafFaceList;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Quick overlap test for brushes
+// Input : *p1 -
+// *p2 -
+// Output : Returns false if the brushes cannot intersect
+//-----------------------------------------------------------------------------
+bool BrushBoxOverlap( bspbrush_t *p1, bspbrush_t *p2 )
+{
+ if ( p1 == p2 )
+ return false;
+
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( p1->mins[i] > p2->maxs[i] || p1->maxs[i] < p2->mins[i] )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFace - input face to test
+// *pbrush - brush to clip face against
+// **pOutputList - list of faces clipped from pFace
+// Output : Returns true if the brush completely clips the face
+//-----------------------------------------------------------------------------
+// NOTE: This assumes the brushes have already been chopped so that no solid space
+// is enclosed by more than one brush!!
+bool ClipFaceToBrush( face_t *pFace, bspbrush_t *pbrush, face_t **pOutputList )
+{
+ int planenum = pFace->planenum & (~1);
+ int foundSide = -1;
+
+ CUtlVector<int> sortedSides;
+
+ int i;
+ for ( i = 0; i < pbrush->numsides && foundSide < 0; i++ )
+ {
+ int bplane = pbrush->sides[i].planenum & (~1);
+ if ( bplane == planenum )
+ foundSide = i;
+ }
+
+ Vector offset = -0.5f * (pbrush->maxs + pbrush->mins);
+ face_t *currentface = CopyFace( pFace );
+
+ if ( foundSide >= 0 )
+ {
+ sortedSides.RemoveAll();
+ for ( i = 0; i < pbrush->numsides; i++ )
+ {
+ // don't clip to bevels
+ if ( pbrush->sides[i].bevel )
+ continue;
+
+ if ( g_MainMap->mapplanes[pbrush->sides[i].planenum].type <= PLANE_Z )
+ {
+ sortedSides.AddToHead( i );
+ }
+ else
+ {
+ sortedSides.AddToTail( i );
+ }
+ }
+
+ for ( i = 0; i < sortedSides.Size(); i++ )
+ {
+ int index = sortedSides[i];
+ if ( index == foundSide )
+ continue;
+
+ plane_t *plane = &g_MainMap->mapplanes[pbrush->sides[index].planenum];
+ winding_t *frontwinding, *backwinding;
+ ClipWindingEpsilon_Offset(currentface->w, plane->normal, plane->dist, 0.001, &frontwinding, &backwinding, offset);
+
+ // only clip if some part of this face is on the back side of all brush sides
+ if ( !backwinding || WindingIsTiny(backwinding))
+ {
+ FreeFaceList( *pOutputList );
+ *pOutputList = NULL;
+ break;
+ }
+ if ( frontwinding && !WindingIsTiny(frontwinding) )
+ {
+ // add this fragment to the return list
+ // make a face for the fragment
+ face_t *f = NewFaceFromFace( pFace );
+ f->w = frontwinding;
+
+ // link the fragment in
+ f->next = *pOutputList;
+ *pOutputList = f;
+ }
+
+ // update the current winding to be the part behind each plane
+ FreeWinding( currentface->w );
+ currentface->w = backwinding;
+ }
+
+ // free the bit that is left in solid or not clipped (if we broke out early)
+ FreeFace( currentface );
+
+ // if we made it all the way through and didn't produce any fragments then the whole face was clipped away
+ if ( !*pOutputList && i == sortedSides.Size() )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given an original side and chopped winding, make a face_t
+// Input : *side - side of the original brush
+// *winding - winding for this face (portion of the side)
+// Output : face_t
+//-----------------------------------------------------------------------------
+face_t *MakeBrushFace( side_t *originalSide, winding_t *winding )
+{
+ face_t *f = AllocFace();
+ f->merged = NULL;
+ f->split[0] = f->split[1] = NULL;
+ f->w = CopyWinding( winding );
+ f->originalface = originalSide;
+ //
+ // save material info
+ //
+ f->texinfo = originalSide->texinfo;
+ f->dispinfo = -1;
+
+ // save plane info
+ f->planenum = originalSide->planenum;
+ f->contents = originalSide->contents;
+
+ return f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Chop away sides that are inside other brushes.
+// Brushes have already been chopped up so that they do not overlap,
+// they merely touch.
+// Input : *list - list of brushes
+// Output : face_t * - list of visible faces (some marked bad/split)
+//-----------------------------------------------------------------------------
+// assumes brushes were chopped!
+
+
+side_t *FindOriginalSide( mapbrush_t *mb, side_t *pBspSide )
+{
+ side_t *bestside = NULL;
+ float bestdot = 0;
+
+ plane_t *p1 = g_MainMap->mapplanes + pBspSide->planenum;
+
+ for (int i=0 ; i<mb->numsides ; i++)
+ {
+ side_t *side = &mb->original_sides[i];
+ if (side->bevel)
+ continue;
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // non-visible
+ if ((side->planenum&~1) == (pBspSide->planenum&~1))
+ { // exact match
+ return mb->original_sides + i;
+ }
+ // see how close the match is
+ plane_t *p2 = &g_MainMap->mapplanes[side->planenum&~1];
+ float dot = DotProduct (p1->normal, p2->normal);
+ if (dot > bestdot)
+ {
+ bestdot = dot;
+ bestside = side;
+ }
+ }
+
+ if ( !bestside )
+ {
+ Error( "Bad detail brush side\n" );
+ }
+ return bestside;
+}
+
+// Get a list of brushes from pBrushList that could cut faces on the source brush
+int GetListOfCutBrushes( CUtlVector<bspbrush_t *> &out, bspbrush_t *pSourceBrush, bspbrush_t *pBrushList )
+{
+ mapbrush_t *mb = pSourceBrush->original;
+ for ( bspbrush_t *walk = pBrushList; walk; walk = walk->next )
+ {
+ if ( walk == pSourceBrush )
+ continue;
+
+ // only clip to transparent brushes if the original brush is transparent
+ if ( walk->original->contents & TRANSPARENT_CONTENTS )
+ {
+ if ( !(mb->contents & TRANSPARENT_CONTENTS) )
+ continue;
+ }
+
+ // don't clip to clip brushes, etc.
+ if ( !(walk->original->contents & ALL_VISIBLE_CONTENTS) )
+ continue;
+
+ // brushes overlap, test faces
+ if ( !BrushBoxOverlap( pSourceBrush, walk ) )
+ continue;
+
+ out.AddToTail( walk );
+ }
+ return out.Count();
+}
+
+// Count the number of real (unsplit) faces in the list
+static int CountFaceList( face_t *f )
+{
+ int count = 0;
+ for ( ; f; f = f->next )
+ {
+ if ( f->split[0] )
+ continue;
+ count++;
+ }
+
+ return count;
+}
+
+// Clips f to a list of potential cutting brushes
+// If f clips into new faces, returns the list of new faces in pOutputList
+static void ClipFaceToBrushList( face_t *f, const CUtlVector<bspbrush_t *> &cutBrushes, face_t **pOutputList )
+{
+ *pOutputList = NULL;
+
+ if ( f->split[0] )
+ return;
+
+ face_t *pClipList = CopyFace( f );
+ pClipList->next = NULL;
+ bool clipped = false;
+ for ( int i = 0; i < cutBrushes.Count(); i++ )
+ {
+ bspbrush_t *cut = cutBrushes[i];
+ for ( face_t *pCutFace = pClipList; pCutFace; pCutFace = pCutFace->next )
+ {
+ face_t *pClip = NULL;
+ // already split, no need to clip
+ if ( pCutFace->split[0] )
+ continue;
+
+ if ( ClipFaceToBrush( pCutFace, cut, &pClip ) )
+ {
+ clipped = true;
+ // mark face bad, the brush clipped it away
+ pCutFace->split[0] = pCutFace;
+ }
+ else if ( pClip )
+ {
+ clipped = true;
+ // mark this face as split
+ pCutFace->split[0] = pCutFace;
+
+ // insert face fragments at head of list (UNDONE: reverses order, do we care?)
+ while ( pClip )
+ {
+ face_t *next = pClip->next;
+ pClip->next = pClipList;
+ pClipList = pClip;
+ pClip = next;
+ }
+ }
+ }
+ }
+ if ( clipped )
+ {
+ *pOutputList = pClipList;
+ }
+ else
+ {
+ // didn't do any clipping, go ahead and free the copy of the face here.
+ FreeFaceList( pClipList );
+ }
+}
+
+// Compute a list of faces that are visible on the detail brush sides
+face_t *ComputeVisibleBrushSides( bspbrush_t *list )
+{
+ face_t *pTotalFaces = NULL;
+ CUtlVector<bspbrush_t *> cutBrushes;
+
+ // Go through the whole brush list
+ for ( bspbrush_t *pbrush = list; pbrush; pbrush = pbrush->next )
+ {
+ face_t *pFaces = NULL;
+ mapbrush_t *mb = pbrush->original;
+
+ if ( !(mb->contents & ALL_VISIBLE_CONTENTS) )
+ continue;
+
+ // Make a face for each brush side, then clip it by the other
+ // details to see if any fragments are visible
+ for ( int i = 0; i < pbrush->numsides; i++ )
+ {
+ winding_t *winding = pbrush->sides[i].winding;
+ if ( !winding )
+ continue;
+
+ if (! (pbrush->sides[i].contents & ALL_VISIBLE_CONTENTS) )
+ continue;
+
+ side_t *side = FindOriginalSide( mb, pbrush->sides + i );
+ face_t *f = MakeBrushFace( side, winding );
+
+ // link to head of face list
+ f->next = pFaces;
+ pFaces = f;
+ }
+
+ // Make a list of brushes that can cut the face list for this brush
+ cutBrushes.RemoveAll();
+ if ( GetListOfCutBrushes( cutBrushes, pbrush, list ) )
+ {
+ // now cut each face to find visible fragments
+ for ( face_t *f = pFaces; f; f = f->next )
+ {
+ // this will be a new list of faces that this face cuts into
+ face_t *pClip = NULL;
+ ClipFaceToBrushList( f, cutBrushes, &pClip );
+ if ( pClip )
+ {
+ int outCount = CountFaceList(pClip);
+ // it cut into more faces (or it was completely cut away)
+ if ( outCount <= 1 )
+ {
+ // was removed or cut down, mark as split
+ f->split[0] = f;
+ // insert face fragments at head of list (UNDONE: reverses order, do we care?)
+ while ( pClip )
+ {
+ face_t *next = pClip->next;
+ pClip->next = pFaces;
+ pFaces = pClip;
+ pClip = next;
+ }
+ }
+ else
+ {
+ // it cut into more than one visible fragment
+ // Don't fragment details
+ // UNDONE: Build 2d convex hull of this list and swap face winding
+ // with that polygon? That would fix the remaining issues.
+ FreeFaceList( pClip );
+ pClip = NULL;
+ }
+ }
+ }
+ }
+
+ // move visible fragments to global face list
+ while ( pFaces )
+ {
+ face_t *next = pFaces->next;
+ if ( pFaces->split[0] )
+ {
+ FreeFace( pFaces );
+ }
+ else
+ {
+ pFaces->next = pTotalFaces;
+ pTotalFaces = pFaces;
+ }
+ pFaces = next;
+ }
+ }
+
+ return pTotalFaces;
+}
diff --git a/mp/src/utils/vbsp/detail.h b/mp/src/utils/vbsp/detail.h new file mode 100644 index 00000000..aa0147d1 --- /dev/null +++ b/mp/src/utils/vbsp/detail.h @@ -0,0 +1,18 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DETAIL_H
+#define DETAIL_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+face_t *MergeDetailTree( tree_t *worldtree, int brush_start, int brush_end );
+
+
+#endif // DETAIL_H
diff --git a/mp/src/utils/vbsp/detailobjects.cpp b/mp/src/utils/vbsp/detailobjects.cpp new file mode 100644 index 00000000..22595781 --- /dev/null +++ b/mp/src/utils/vbsp/detailobjects.cpp @@ -0,0 +1,966 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Places "detail" objects which are client-only renderable things
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include <windows.h>
+#include "vbsp.h"
+#include "bsplib.h"
+#include "KeyValues.h"
+#include "utlsymbol.h"
+#include "utlvector.h"
+#include <io.h>
+#include "bspfile.h"
+#include "utilmatlib.h"
+#include "gamebspfile.h"
+#include "mathlib/VMatrix.h"
+#include "materialpatch.h"
+#include "pacifier.h"
+#include "vstdlib/random.h"
+#include "builddisp.h"
+#include "disp_vbsp.h"
+#include "UtlBuffer.h"
+#include "CollisionUtils.h"
+#include <float.h>
+#include "UtlLinkedList.h"
+#include "byteswap.h"
+#include "writebsp.h"
+
+//-----------------------------------------------------------------------------
+// Information about particular detail object types
+//-----------------------------------------------------------------------------
+enum
+{
+ MODELFLAG_UPRIGHT = 0x1,
+};
+
+struct DetailModel_t
+{
+ CUtlSymbol m_ModelName;
+ float m_Amount;
+ float m_MinCosAngle;
+ float m_MaxCosAngle;
+ int m_Flags;
+ int m_Orientation;
+ int m_Type;
+ Vector2D m_Pos[2];
+ Vector2D m_Tex[2];
+ float m_flRandomScaleStdDev;
+ unsigned char m_ShapeSize;
+ unsigned char m_ShapeAngle;
+ unsigned char m_SwayAmount;
+};
+
+struct DetailObjectGroup_t
+{
+ float m_Alpha;
+ CUtlVector< DetailModel_t > m_Models;
+};
+
+struct DetailObject_t
+{
+ CUtlSymbol m_Name;
+ float m_Density;
+ CUtlVector< DetailObjectGroup_t > m_Groups;
+
+ bool operator==(const DetailObject_t& src ) const
+ {
+ return src.m_Name == m_Name;
+ }
+};
+
+static CUtlVector<DetailObject_t> s_DetailObjectDict;
+
+
+//-----------------------------------------------------------------------------
+// Error checking.. make sure the model is valid + is a static prop
+//-----------------------------------------------------------------------------
+struct StaticPropLookup_t
+{
+ CUtlSymbol m_ModelName;
+ bool m_IsValid;
+};
+
+static bool StaticLess( StaticPropLookup_t const& src1, StaticPropLookup_t const& src2 )
+{
+ return src1.m_ModelName < src2.m_ModelName;
+}
+
+static CUtlRBTree< StaticPropLookup_t, unsigned short > s_StaticPropLookup( 0, 32, StaticLess );
+
+
+//-----------------------------------------------------------------------------
+// These puppies are used to construct the game lumps
+//-----------------------------------------------------------------------------
+static CUtlVector<DetailObjectDictLump_t> s_DetailObjectDictLump;
+static CUtlVector<DetailObjectLump_t> s_DetailObjectLump;
+static CUtlVector<DetailSpriteDictLump_t> s_DetailSpriteDictLump;
+
+
+//-----------------------------------------------------------------------------
+// Parses the key-value pairs in the detail.rad file
+//-----------------------------------------------------------------------------
+static void ParseDetailGroup( int detailId, KeyValues* pGroupKeyValues )
+{
+ // Sort the group by alpha
+ float alpha = pGroupKeyValues->GetFloat( "alpha", 1.0f );
+
+ int i = s_DetailObjectDict[detailId].m_Groups.Count();
+ while ( --i >= 0 )
+ {
+ if (alpha > s_DetailObjectDict[detailId].m_Groups[i].m_Alpha)
+ break;
+ }
+
+ // Insert after the first guy who's more transparent that we are!
+ i = s_DetailObjectDict[detailId].m_Groups.InsertAfter(i);
+ DetailObjectGroup_t& group = s_DetailObjectDict[detailId].m_Groups[i];
+
+ group.m_Alpha = alpha;
+
+ // Add in all the model groups
+ KeyValues* pIter = pGroupKeyValues->GetFirstSubKey();
+ float totalAmount = 0.0f;
+ while( pIter )
+ {
+ if (pIter->GetFirstSubKey())
+ {
+ int i = group.m_Models.AddToTail();
+
+ DetailModel_t &model = group.m_Models[i];
+
+ model.m_ModelName = pIter->GetString( "model", 0 );
+ if (model.m_ModelName != UTL_INVAL_SYMBOL)
+ {
+ model.m_Type = DETAIL_PROP_TYPE_MODEL;
+ }
+ else
+ {
+ const char *pSpriteData = pIter->GetString( "sprite", 0 );
+ if (pSpriteData)
+ {
+ const char *pProcModelType = pIter->GetString( "sprite_shape", 0 );
+
+ if ( pProcModelType )
+ {
+ if ( !Q_stricmp( pProcModelType, "cross" ) )
+ {
+ model.m_Type = DETAIL_PROP_TYPE_SHAPE_CROSS;
+ }
+ else if ( !Q_stricmp( pProcModelType, "tri" ) )
+ {
+ model.m_Type = DETAIL_PROP_TYPE_SHAPE_TRI;
+ }
+ else
+ model.m_Type = DETAIL_PROP_TYPE_SPRITE;
+ }
+ else
+ {
+ // card sprite
+ model.m_Type = DETAIL_PROP_TYPE_SPRITE;
+ }
+
+ model.m_Tex[0].Init();
+ model.m_Tex[1].Init();
+
+ float x = 0, y = 0, flWidth = 64, flHeight = 64, flTextureSize = 512;
+ int nValid = sscanf( pSpriteData, "%f %f %f %f %f", &x, &y, &flWidth, &flHeight, &flTextureSize );
+ if ( (nValid != 5) || (flTextureSize == 0) )
+ {
+ Error( "Invalid arguments to \"sprite\" in detail.vbsp (model %s)!\n", model.m_ModelName.String() );
+ }
+
+ model.m_Tex[0].x = ( x + 0.5f ) / flTextureSize;
+ model.m_Tex[0].y = ( y + 0.5f ) / flTextureSize;
+ model.m_Tex[1].x = ( x + flWidth - 0.5f ) / flTextureSize;
+ model.m_Tex[1].y = ( y + flHeight - 0.5f ) / flTextureSize;
+
+ model.m_Pos[0].Init( -10, 20 );
+ model.m_Pos[1].Init( 10, 0 );
+
+ pSpriteData = pIter->GetString( "spritesize", 0 );
+ if (pSpriteData)
+ {
+ sscanf( pSpriteData, "%f %f %f %f", &x, &y, &flWidth, &flHeight );
+
+ float ox = flWidth * x;
+ float oy = flHeight * y;
+
+ model.m_Pos[0].x = -ox;
+ model.m_Pos[0].y = flHeight - oy;
+ model.m_Pos[1].x = flWidth - ox;
+ model.m_Pos[1].y = -oy;
+ }
+
+ model.m_flRandomScaleStdDev = pIter->GetFloat( "spriterandomscale", 0.0f );
+
+ // sway is a percent of max sway, cl_detail_max_sway
+ float flSway = clamp( pIter->GetFloat( "sway", 0.0f ), 0.0, 1.0 );
+ model.m_SwayAmount = (unsigned char)( 255.0 * flSway );
+
+ // shape angle
+ // for the tri shape, this is the angle each side is fanned out
+ model.m_ShapeAngle = pIter->GetInt( "shape_angle", 0 );
+
+ // shape size
+ // for the tri shape, this is the distance from the origin to the center of a side
+ float flShapeSize = clamp( pIter->GetFloat( "shape_size", 0.0f ), 0.0, 1.0 );
+ model.m_ShapeSize = (unsigned char)( 255.0 * flShapeSize );
+ }
+ }
+
+ model.m_Amount = pIter->GetFloat( "amount", 1.0 ) + totalAmount;
+ totalAmount = model.m_Amount;
+
+ model.m_Flags = 0;
+ if (pIter->GetInt( "upright", 0 ))
+ {
+ model.m_Flags |= MODELFLAG_UPRIGHT;
+ }
+
+ // These are used to prevent emission on steep surfaces
+ float minAngle = pIter->GetFloat( "minAngle", 180 );
+ float maxAngle = pIter->GetFloat( "maxAngle", 180 );
+ model.m_MinCosAngle = cos(minAngle * M_PI / 180.f);
+ model.m_MaxCosAngle = cos(maxAngle * M_PI / 180.f);
+ model.m_Orientation = pIter->GetInt( "detailOrientation", 0 );
+
+ // Make sure minAngle < maxAngle
+ if ( model.m_MinCosAngle < model.m_MaxCosAngle)
+ {
+ model.m_MinCosAngle = model.m_MaxCosAngle;
+ }
+ }
+ pIter = pIter->GetNextKey();
+ }
+
+ // renormalize the amount if the total > 1
+ if (totalAmount > 1.0f)
+ {
+ for (i = 0; i < group.m_Models.Count(); ++i)
+ {
+ group.m_Models[i].m_Amount /= totalAmount;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses the key-value pairs in the detail.vbsp file
+//-----------------------------------------------------------------------------
+static void ParseDetailObjectFile( KeyValues& keyValues )
+{
+ // Iterate over all detail object groups...
+ KeyValues* pIter;
+ for( pIter = keyValues.GetFirstSubKey(); pIter; pIter = pIter->GetNextKey() )
+ {
+ if (!pIter->GetFirstSubKey())
+ continue;
+
+ int i = s_DetailObjectDict.AddToTail( );
+ s_DetailObjectDict[i].m_Name = pIter->GetName() ;
+ s_DetailObjectDict[i].m_Density = pIter->GetFloat( "density", 0.0f );
+
+ // Iterate over all detail object groups...
+ KeyValues* pIterGroups = pIter->GetFirstSubKey();
+ while( pIterGroups )
+ {
+ if (pIterGroups->GetFirstSubKey())
+ {
+ ParseDetailGroup( i, pIterGroups );
+ }
+ pIterGroups = pIterGroups->GetNextKey();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the name of the detail.vbsp file to use
+//-----------------------------------------------------------------------------
+static const char *FindDetailVBSPName( void )
+{
+ for( int i = 0; i < num_entities; i++ )
+ {
+ char* pEntity = ValueForKey( &entities[i], "classname" );
+ if ( !strcmp( pEntity, "worldspawn" ) )
+ {
+ const char *pDetailVBSP = ValueForKey( &entities[i], "detailvbsp" );
+ if ( !pDetailVBSP || !pDetailVBSP[0] )
+ {
+ pDetailVBSP = "detail.vbsp";
+ }
+ return pDetailVBSP;
+ }
+ }
+ return "detail.vbsp";
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads up the detail object dictionary
+//-----------------------------------------------------------------------------
+void LoadEmitDetailObjectDictionary( const char* pGameDir )
+{
+ // Set the required global lights filename and try looking in qproject
+ const char *pDetailVBSP = FindDetailVBSPName();
+ KeyValues * values = new KeyValues( pDetailVBSP );
+ if ( values->LoadFromFile( g_pFileSystem, pDetailVBSP ) )
+ {
+ ParseDetailObjectFile( *values );
+ }
+ values->deleteThis();
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a detail group
+//-----------------------------------------------------------------------------
+static int SelectGroup( const DetailObject_t& detail, float alpha )
+{
+ // Find the two groups whose alpha we're between...
+ int start, end;
+ for ( start = 0; start < detail.m_Groups.Count() - 1; ++start )
+ {
+ if (alpha < detail.m_Groups[start+1].m_Alpha)
+ break;
+ }
+
+ end = start + 1;
+ if (end >= detail.m_Groups.Count())
+ --end;
+
+ if (start == end)
+ return start;
+
+ // Figure out how far we are between start and end...
+ float dist = 0.0f;
+ float dAlpha = (detail.m_Groups[end].m_Alpha - detail.m_Groups[start].m_Alpha);
+ if (dAlpha != 0.0f)
+ {
+ dist = (alpha - detail.m_Groups[start].m_Alpha) / dAlpha;
+ }
+
+ // Pick a number, any number...
+ float r = rand() / (float)VALVE_RAND_MAX;
+
+ // When dist == 0, we *always* want start.
+ // When dist == 1, we *always* want end
+ // That's why this logic looks a little reversed
+ return (r > dist) ? start : end;
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a detail object
+//-----------------------------------------------------------------------------
+static int SelectDetail( DetailObjectGroup_t const& group )
+{
+ // Pick a number, any number...
+ float r = rand() / (float)VALVE_RAND_MAX;
+
+ // Look through the list of models + pick the one associated with this number
+ for ( int i = 0; i < group.m_Models.Count(); ++i )
+ {
+ if (r <= group.m_Models[i].m_Amount)
+ return i;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a detail dictionary element (expected to oftentimes be shared)
+//-----------------------------------------------------------------------------
+static int AddDetailDictLump( const char* pModelName )
+{
+ DetailObjectDictLump_t dictLump;
+ strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
+
+ for (int i = s_DetailObjectDictLump.Count(); --i >= 0; )
+ {
+ if (!memcmp(&s_DetailObjectDictLump[i], &dictLump, sizeof(dictLump) ))
+ return i;
+ }
+
+ return s_DetailObjectDictLump.AddToTail( dictLump );
+}
+
+static int AddDetailSpriteDictLump( const Vector2D *pPos, const Vector2D *pTex )
+{
+ DetailSpriteDictLump_t dictLump;
+ dictLump.m_UL = pPos[0];
+ dictLump.m_LR = pPos[1];
+ dictLump.m_TexUL = pTex[0];
+ dictLump.m_TexLR = pTex[1];
+
+ for (int i = s_DetailSpriteDictLump.Count(); --i >= 0; )
+ {
+ if (!memcmp(&s_DetailSpriteDictLump[i], &dictLump, sizeof(dictLump) ))
+ return i;
+ }
+
+ return s_DetailSpriteDictLump.AddToTail( dictLump );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the leaf that the detail lies in
+//-----------------------------------------------------------------------------
+static int ComputeDetailLeaf( const Vector& pt )
+{
+ int node = 0;
+ while( node >= 0 )
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ if (DotProduct(pt, pPlane->normal) < pPlane->dist)
+ node = pNode->children[1];
+ else
+ node = pNode->children[0];
+ }
+
+ return - node - 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Make sure the details are compiled with static prop
+//-----------------------------------------------------------------------------
+static bool IsModelValid( const char* pModelName )
+{
+ StaticPropLookup_t lookup;
+ lookup.m_ModelName = pModelName;
+
+ int i = s_StaticPropLookup.Find( lookup );
+ if (i != s_StaticPropLookup.InvalidIndex() )
+ return s_StaticPropLookup[i].m_IsValid;
+
+ CUtlBuffer buf;
+ lookup.m_IsValid = LoadStudioModel( pModelName, "detail_prop", buf );
+ if (!lookup.m_IsValid)
+ {
+ Warning("Error loading studio model \"%s\"!\n", pModelName );
+ }
+
+ s_StaticPropLookup.Insert( lookup );
+ return lookup.m_IsValid;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a detail to the lump.
+//-----------------------------------------------------------------------------
+static int s_nDetailOverflow = 0;
+static void AddDetailToLump( const char* pModelName, const Vector& pt, const QAngle& angles, int nOrientation )
+{
+ Assert( pt.IsValid() && angles.IsValid() );
+
+ // Make sure the model is valid...
+ if (!IsModelValid(pModelName))
+ return;
+
+ if (s_DetailObjectLump.Count() == 65535)
+ {
+ ++s_nDetailOverflow;
+ return;
+ }
+
+ // Insert an element into the object dictionary if it aint there...
+ int i = s_DetailObjectLump.AddToTail( );
+
+ DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
+ objectLump.m_DetailModel = AddDetailDictLump( pModelName );
+ VectorCopy( angles, objectLump.m_Angles );
+ VectorCopy( pt, objectLump.m_Origin );
+ objectLump.m_Leaf = ComputeDetailLeaf(pt);
+ objectLump.m_Lighting.r = 255;
+ objectLump.m_Lighting.g = 255;
+ objectLump.m_Lighting.b = 255;
+ objectLump.m_Lighting.exponent = 0;
+ objectLump.m_LightStyles = 0;
+ objectLump.m_LightStyleCount = 0;
+ objectLump.m_Orientation = nOrientation;
+ objectLump.m_Type = DETAIL_PROP_TYPE_MODEL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a detail sprite to the lump.
+//-----------------------------------------------------------------------------
+static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, int nOrientation,
+ const Vector2D *pPos, const Vector2D *pTex, float flScale, int iType,
+ int iShapeAngle = 0, int iShapeSize = 0, int iSwayAmount = 0 )
+{
+ // Insert an element into the object dictionary if it aint there...
+ int i = s_DetailObjectLump.AddToTail( );
+
+ if (i >= 65535)
+ {
+ Error( "Error! Too many detail props emitted on this map! (64K max!)n" );
+ }
+
+ DetailObjectLump_t& objectLump = s_DetailObjectLump[i];
+ objectLump.m_DetailModel = AddDetailSpriteDictLump( pPos, pTex );
+ VectorCopy( vecAngles, objectLump.m_Angles );
+ VectorCopy( vecOrigin, objectLump.m_Origin );
+ objectLump.m_Leaf = ComputeDetailLeaf(vecOrigin);
+ objectLump.m_Lighting.r = 255;
+ objectLump.m_Lighting.g = 255;
+ objectLump.m_Lighting.b = 255;
+ objectLump.m_Lighting.exponent = 0;
+ objectLump.m_LightStyles = 0;
+ objectLump.m_LightStyleCount = 0;
+ objectLump.m_Orientation = nOrientation;
+ objectLump.m_Type = iType;
+ objectLump.m_flScale = flScale;
+ objectLump.m_ShapeAngle = iShapeAngle;
+ objectLump.m_ShapeSize = iShapeSize;
+ objectLump.m_SwayAmount = iSwayAmount;
+}
+
+static void AddDetailSpriteToLump( const Vector &vecOrigin, const QAngle &vecAngles, DetailModel_t const& model, float flScale )
+{
+ AddDetailSpriteToLump( vecOrigin,
+ vecAngles,
+ model.m_Orientation,
+ model.m_Pos,
+ model.m_Tex,
+ flScale,
+ model.m_Type,
+ model.m_ShapeAngle,
+ model.m_ShapeSize,
+ model.m_SwayAmount );
+}
+
+//-----------------------------------------------------------------------------
+// Got a detail! Place it on the surface...
+//-----------------------------------------------------------------------------
+// BUGBUG: When the global optimizer is on, "normal" gets trashed in this function
+// (only when not in the debugger?)
+// Printing the values of normal at the bottom of the function fixes it as does
+// disabling global optimizations.
+static void PlaceDetail( DetailModel_t const& model, const Vector& pt, const Vector& normal )
+{
+ // But only place it on the surface if it meets the angle constraints...
+ float cosAngle = normal.z;
+
+ // Never emit if the angle's too steep
+ if (cosAngle < model.m_MaxCosAngle)
+ return;
+
+ // If it's between min + max, flip a coin...
+ if (cosAngle < model.m_MinCosAngle)
+ {
+ float probability = (cosAngle - model.m_MaxCosAngle) /
+ (model.m_MinCosAngle - model.m_MaxCosAngle);
+
+ float t = rand() / (float)VALVE_RAND_MAX;
+ if (t > probability)
+ return;
+ }
+
+ // Compute the orientation of the detail
+ QAngle angles;
+ if (model.m_Flags & MODELFLAG_UPRIGHT)
+ {
+ // If it's upright, we just select a random yaw
+ angles.Init( 0, 360.0f * rand() / (float)VALVE_RAND_MAX, 0.0f );
+ }
+ else
+ {
+ // It's not upright, so it must conform to the ground. Choose
+ // a random orientation based on the surface normal
+
+ Vector zaxis;
+ VectorCopy( normal, zaxis );
+ VectorNormalize( zaxis );
+
+ // Choose any two arbitrary axes which are perpendicular to the normal
+ Vector xaxis( 1, 0, 0 );
+ if (fabs(xaxis.Dot(zaxis)) - 1.0 > -1e-3)
+ xaxis.Init( 0, 1, 0 );
+ Vector yaxis;
+ CrossProduct( zaxis, xaxis, yaxis );
+ VectorNormalize( yaxis );
+ CrossProduct( yaxis, zaxis, xaxis );
+ VectorNormalize( xaxis );
+ VMatrix matrix;
+ matrix.SetBasisVectors( xaxis, yaxis, zaxis );
+ matrix.SetTranslation( vec3_origin );
+
+ float rotAngle = 360.0f * rand() / (float)VALVE_RAND_MAX;
+ VMatrix rot = SetupMatrixAxisRot( Vector( 0, 0, 1 ), rotAngle );
+ matrix = matrix * rot;
+
+ MatrixToAngles( matrix, angles );
+ }
+
+ // FIXME: We may also want a purely random rotation too
+
+ // Insert an element into the object dictionary if it aint there...
+ switch ( model.m_Type )
+ {
+ case DETAIL_PROP_TYPE_MODEL:
+ AddDetailToLump( model.m_ModelName.String(), pt, angles, model.m_Orientation );
+ break;
+
+ // Sprites and procedural models made from sprites
+ case DETAIL_PROP_TYPE_SPRITE:
+ default:
+ {
+ float flScale = 1.0f;
+ if ( model.m_flRandomScaleStdDev != 0.0f )
+ {
+ flScale = fabs( RandomGaussianFloat( 1.0f, model.m_flRandomScaleStdDev ) );
+ }
+
+ AddDetailSpriteToLump( pt, angles, model, flScale );
+ }
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects on a face
+//-----------------------------------------------------------------------------
+static void EmitDetailObjectsOnFace( dface_t* pFace, DetailObject_t& detail )
+{
+ if (pFace->numedges < 3)
+ return;
+
+ // We're going to pick a bunch of random points, and then probabilistically
+ // decide whether or not to plant a detail object there.
+
+ // Turn the face into a bunch of polygons, and compute the area of each
+ int* pSurfEdges = &dsurfedges[pFace->firstedge];
+ int vertexIdx = (pSurfEdges[0] < 0);
+ int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
+ dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
+ for (int i = 1; i < pFace->numedges - 1; ++i )
+ {
+ int vertexIdx = (pSurfEdges[i] < 0);
+ dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
+
+ // Compute two triangle edges
+ Vector e1, e2;
+ VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
+ VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
+
+ // Compute the triangle area
+ Vector areaVec;
+ CrossProduct( e1, e2, areaVec );
+ float normalLength = areaVec.Length();
+ float area = 0.5f * normalLength;
+
+ // Compute the number of samples to take
+ int numSamples = area * detail.m_Density * 0.000001;
+
+ // Now take a sample, and randomly place an object there
+ for (int i = 0; i < numSamples; ++i )
+ {
+ // Create a random sample...
+ float u = rand() / (float)VALVE_RAND_MAX;
+ float v = rand() / (float)VALVE_RAND_MAX;
+ if (v > 1.0f - u)
+ {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ assert( u + v <= 1.0f );
+ }
+
+ // Compute alpha
+ float alpha = 1.0f;
+
+ // Select a group based on the alpha value
+ int group = SelectGroup( detail, alpha );
+
+ // Now that we've got a group, choose a detail
+ int model = SelectDetail( detail.m_Groups[group] );
+ if (model < 0)
+ continue;
+
+ // Got a detail! Place it on the surface...
+ Vector pt, normal;
+ VectorMA( pFirstVertex->point, u, e1, pt );
+ VectorMA( pt, v, e2, pt );
+ VectorDivide( areaVec, -normalLength, normal );
+
+ PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects on a face
+//-----------------------------------------------------------------------------
+static float ComputeDisplacementFaceArea( dface_t* pFace )
+{
+ float area = 0.0f;
+
+ // Compute the area of the base face
+ int* pSurfEdges = &dsurfedges[pFace->firstedge];
+ int vertexIdx = (pSurfEdges[0] < 0);
+ int firstVertexIndex = dedges[abs(pSurfEdges[0])].v[vertexIdx];
+ dvertex_t* pFirstVertex = &dvertexes[firstVertexIndex];
+ for (int i = 1; i <= 2; ++i )
+ {
+ int vertexIdx = (pSurfEdges[i] < 0);
+ dedge_t* pEdge = &dedges[abs(pSurfEdges[i])];
+
+ // Compute two triangle edges
+ Vector e1, e2;
+ VectorSubtract( dvertexes[pEdge->v[vertexIdx]].point, pFirstVertex->point, e1 );
+ VectorSubtract( dvertexes[pEdge->v[1 - vertexIdx]].point, pFirstVertex->point, e2 );
+
+ // Compute the triangle area
+ Vector areaVec;
+ CrossProduct( e1, e2, areaVec );
+ float normalLength = areaVec.Length();
+ area += 0.5f * normalLength;
+ }
+
+ return area;
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects on a face
+//-----------------------------------------------------------------------------
+static void EmitDetailObjectsOnDisplacementFace( dface_t* pFace,
+ DetailObject_t& detail, CCoreDispInfo& coreDispInfo )
+{
+ assert(pFace->numedges == 4);
+
+ // We're going to pick a bunch of random points, and then probabilistically
+ // decide whether or not to plant a detail object there.
+
+ // Compute the area of the base face
+ float area = ComputeDisplacementFaceArea( pFace );
+
+ // Compute the number of samples to take
+ int numSamples = area * detail.m_Density * 0.000001;
+
+ // Now take a sample, and randomly place an object there
+ for (int i = 0; i < numSamples; ++i )
+ {
+ // Create a random sample...
+ float u = rand() / (float)VALVE_RAND_MAX;
+ float v = rand() / (float)VALVE_RAND_MAX;
+
+ // Compute alpha
+ float alpha;
+ Vector pt, normal;
+ coreDispInfo.GetPositionOnSurface( u, v, pt, &normal, &alpha );
+ alpha /= 255.0f;
+
+ // Select a group based on the alpha value
+ int group = SelectGroup( detail, alpha );
+
+ // Now that we've got a group, choose a detail
+ int model = SelectDetail( detail.m_Groups[group] );
+ if (model < 0)
+ continue;
+
+ // Got a detail! Place it on the surface...
+ PlaceDetail( detail.m_Groups[group].m_Models[model], pt, normal );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sort detail objects by leaf
+//-----------------------------------------------------------------------------
+static int SortFunc( const void *arg1, const void *arg2 )
+{
+ int nDelta = ((DetailObjectLump_t*)arg1)->m_Leaf - ((DetailObjectLump_t*)arg2)->m_Leaf;
+ if ( nDelta < 0 )
+ return -1;
+ if ( nDelta > 0 )
+ return 1;
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects in the lump
+//-----------------------------------------------------------------------------
+static void SetLumpData( )
+{
+ // Sort detail props by leaf
+ qsort( s_DetailObjectLump.Base(), s_DetailObjectLump.Count(), sizeof(DetailObjectLump_t), SortFunc );
+
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_DETAIL_PROPS);
+ if (handle != g_GameLumps.InvalidGameLump())
+ {
+ g_GameLumps.DestroyGameLump(handle);
+ }
+ int nDictSize = s_DetailObjectDictLump.Count() * sizeof(DetailObjectDictLump_t);
+ int nSpriteDictSize = s_DetailSpriteDictLump.Count() * sizeof(DetailSpriteDictLump_t);
+ int nObjSize = s_DetailObjectLump.Count() * sizeof(DetailObjectLump_t);
+ int nSize = nDictSize + nSpriteDictSize + nObjSize + (3 * sizeof(int));
+
+ handle = g_GameLumps.CreateGameLump( GAMELUMP_DETAIL_PROPS, nSize, 0, GAMELUMP_DETAIL_PROPS_VERSION );
+
+ // Serialize the data
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), nSize );
+ buf.PutInt( s_DetailObjectDictLump.Count() );
+ if (nDictSize)
+ {
+ buf.Put( s_DetailObjectDictLump.Base(), nDictSize );
+ }
+ buf.PutInt( s_DetailSpriteDictLump.Count() );
+ if (nSpriteDictSize)
+ {
+ buf.Put( s_DetailSpriteDictLump.Base(), nSpriteDictSize );
+ }
+ buf.PutInt( s_DetailObjectLump.Count() );
+ if (nObjSize)
+ {
+ buf.Put( s_DetailObjectLump.Base(), nObjSize );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects in the level
+//-----------------------------------------------------------------------------
+void EmitDetailModels()
+{
+ StartPacifier("Placing detail props : ");
+
+ // Place stuff on each face
+ dface_t* pFace = dfaces;
+ for (int j = 0; j < numfaces; ++j)
+ {
+ UpdatePacifier( (float)j / (float)numfaces );
+
+ // Get at the material associated with this face
+ texinfo_t* pTexInfo = &texinfo[pFace[j].texinfo];
+ dtexdata_t* pTexData = GetTexData( pTexInfo->texdata );
+
+ // Try to get at the material
+ bool found;
+ MaterialSystemMaterial_t handle =
+ FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ),
+ &found, false );
+ if (!found)
+ continue;
+
+ // See if its got any detail objects on it
+ const char* pDetailType = GetMaterialVar( handle, "%detailtype" );
+ if (!pDetailType)
+ continue;
+
+ // Get the detail type...
+ DetailObject_t search;
+ search.m_Name = pDetailType;
+ int objectType = s_DetailObjectDict.Find(search);
+ if (objectType < 0)
+ {
+ Warning("Material %s uses unknown detail object type %s!\n",
+ TexDataStringTable_GetString( pTexData->nameStringTableID ),
+ pDetailType);
+ continue;
+ }
+
+ // Emit objects on a particular face
+ DetailObject_t& detail = s_DetailObjectDict[objectType];
+
+ // Initialize the Random Number generators for detail prop placement based on the hammer Face num.
+ int detailpropseed = dfaceids[j].hammerfaceid;
+#ifdef WARNSEEDNUMBER
+ Warning( "[%d]\n",detailpropseed );
+#endif
+ srand( detailpropseed );
+ RandomSeed( detailpropseed );
+
+ if (pFace[j].dispinfo < 0)
+ {
+ EmitDetailObjectsOnFace( &pFace[j], detail );
+ }
+ else
+ {
+ // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
+ mapdispinfo_t *pMapDisp = &mapdispinfo[pFace[j].dispinfo];
+ CCoreDispInfo coreDispInfo;
+ DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
+
+ EmitDetailObjectsOnDisplacementFace( &pFace[j], detail, coreDispInfo );
+ }
+ }
+
+ // Emit specifically specified detail props
+ Vector origin;
+ QAngle angles;
+ Vector2D pos[2];
+ Vector2D tex[2];
+ for (int i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "detail_prop") || !strcmp(pEntity, "prop_detail"))
+ {
+ GetVectorForKey( &entities[i], "origin", origin );
+ GetAnglesForKey( &entities[i], "angles", angles );
+ char* pModelName = ValueForKey( &entities[i], "model" );
+ int nOrientation = IntForKey( &entities[i], "detailOrientation" );
+
+ AddDetailToLump( pModelName, origin, angles, nOrientation );
+
+ // strip this ent from the .bsp file
+ entities[i].epairs = 0;
+ continue;
+ }
+
+ if (!strcmp(pEntity, "prop_detail_sprite"))
+ {
+ GetVectorForKey( &entities[i], "origin", origin );
+ GetAnglesForKey( &entities[i], "angles", angles );
+ int nOrientation = IntForKey( &entities[i], "detailOrientation" );
+ GetVector2DForKey( &entities[i], "position_ul", pos[0] );
+ GetVector2DForKey( &entities[i], "position_lr", pos[1] );
+ GetVector2DForKey( &entities[i], "tex_ul", tex[0] );
+ GetVector2DForKey( &entities[i], "tex_size", tex[1] );
+ float flTextureSize = FloatForKey( &entities[i], "tex_total_size" );
+
+ tex[1].x += tex[0].x - 0.5f;
+ tex[1].y += tex[0].y - 0.5f;
+ tex[0].x += 0.5f;
+ tex[0].y += 0.5f;
+ tex[0] /= flTextureSize;
+ tex[1] /= flTextureSize;
+
+ AddDetailSpriteToLump( origin, angles, nOrientation, pos, tex, 1.0f, DETAIL_PROP_TYPE_SPRITE );
+
+ // strip this ent from the .bsp file
+ entities[i].epairs = 0;
+ continue;
+ }
+ }
+
+ EndPacifier( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Detail Objects in the level
+//-----------------------------------------------------------------------------
+void EmitDetailObjects()
+{
+ EmitDetailModels();
+
+ // Done! Now lets add the lumps (destroy previous ones)
+ SetLumpData( );
+
+ if ( s_nDetailOverflow != 0 )
+ {
+ Warning( "Error! Too many detail props on this map. %d were not emitted!\n", s_nDetailOverflow );
+ }
+}
diff --git a/mp/src/utils/vbsp/disp_ivp.cpp b/mp/src/utils/vbsp/disp_ivp.cpp new file mode 100644 index 00000000..3fe50799 --- /dev/null +++ b/mp/src/utils/vbsp/disp_ivp.cpp @@ -0,0 +1,359 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+#include "vbsp.h"
+#include "disp_vbsp.h"
+#include "builddisp.h"
+#include "disp_common.h"
+#include "ivp.h"
+#include "disp_ivp.h"
+#include "vphysics_interface.h"
+#include "vphysics/virtualmesh.h"
+#include "utlrbtree.h"
+#include "tier1/utlbuffer.h"
+#include "materialpatch.h"
+
+struct disp_grid_t
+{
+ int gridIndex;
+ CUtlVector<int> dispList;
+};
+
+static CUtlVector<disp_grid_t> gDispGridList;
+
+
+
+disp_grid_t &FindOrInsertGrid( int gridIndex )
+{
+ // linear search is slow, but only a few grids will be present
+ for ( int i = gDispGridList.Count()-1; i >= 0; i-- )
+ {
+ if ( gDispGridList[i].gridIndex == gridIndex )
+ {
+ return gDispGridList[i];
+ }
+ }
+ int index = gDispGridList.AddToTail();
+ gDispGridList[index].gridIndex = gridIndex;
+
+ // must be empty
+ Assert( gDispGridList[index].dispList.Count() == 0 );
+
+ return gDispGridList[index];
+}
+
+// UNDONE: Tune these or adapt them to map size or triangle count?
+#define DISP_GRID_SIZEX 4096
+#define DISP_GRID_SIZEY 4096
+#define DISP_GRID_SIZEZ 8192
+
+int Disp_GridIndex( CCoreDispInfo *pDispInfo )
+{
+ // quick hash the center into the grid and put the whole terrain in that grid
+ Vector mins, maxs;
+ pDispInfo->GetNode(0)->GetBoundingBox( mins, maxs );
+ Vector center;
+ center = 0.5 * (mins + maxs);
+ // make sure it's positive
+ center += Vector(MAX_COORD_INTEGER,MAX_COORD_INTEGER,MAX_COORD_INTEGER);
+ int gridX = center.x / DISP_GRID_SIZEX;
+ int gridY = center.y / DISP_GRID_SIZEY;
+ int gridZ = center.z / DISP_GRID_SIZEZ;
+
+ gridX &= 0xFF;
+ gridY &= 0xFF;
+ gridZ &= 0xFF;
+ return MAKEID( gridX, gridY, gridZ, 0 );
+}
+
+void AddToGrid( int gridIndex, int dispIndex )
+{
+ disp_grid_t &grid = FindOrInsertGrid( gridIndex );
+ grid.dispList.AddToTail( dispIndex );
+}
+
+MaterialSystemMaterial_t GetMatIDFromDisp( mapdispinfo_t *pMapDisp )
+{
+ texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ MaterialSystemMaterial_t matID = FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ), NULL, true );
+ return matID;
+}
+
+// check this and disable virtual mesh if in use
+bool Disp_HasPower4Displacements()
+{
+ for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
+ {
+ if ( g_CoreDispInfos[i]->GetPower() > 3 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// adds all displacement faces as a series of convex objects
+// UNDONE: Only add the displacements for this model?
+void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask)
+{
+ int dispIndex;
+
+ // Add each displacement to the grid hash
+ for ( dispIndex = 0; dispIndex < g_CoreDispInfos.Count(); dispIndex++ )
+ {
+ CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
+ mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
+
+ // not solid for this pass
+ if ( !(pMapDisp->contents & contentsMask) )
+ continue;
+
+ int gridIndex = Disp_GridIndex( pDispInfo );
+ AddToGrid( gridIndex, dispIndex );
+ }
+
+ // now make a polysoup for the terrain in each grid
+ for ( int grid = 0; grid < gDispGridList.Count(); grid++ )
+ {
+ int triCount = 0;
+ CPhysPolysoup *pTerrainPhysics = physcollision->PolysoupCreate();
+
+ // iterate the displacements in this grid
+ for ( int listIndex = 0; listIndex < gDispGridList[grid].dispList.Count(); listIndex++ )
+ {
+ dispIndex = gDispGridList[grid].dispList[listIndex];
+ CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
+ mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
+
+ // Get the material id.
+ MaterialSystemMaterial_t matID = GetMatIDFromDisp( pMapDisp );
+
+ // Build a triangle list. This shares the tesselation code with the engine.
+ CUtlVector<unsigned short> indices;
+ CVBSPTesselateHelper helper;
+ helper.m_pIndices = &indices;
+ helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
+ helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
+
+ ::TesselateDisplacement( &helper );
+
+ Assert( indices.Count() > 0 );
+ Assert( indices.Count() % 3 == 0 ); // Make sure indices are a multiple of 3.
+ int nTriCount = indices.Count() / 3;
+ triCount += nTriCount;
+ if ( triCount >= 65536 )
+ {
+ // don't put more than 64K tris in any single collision model
+ CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
+ if ( pCollide )
+ {
+ collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
+ }
+ // Throw this polysoup away and start over for the remaining triangles
+ physcollision->PolysoupDestroy( pTerrainPhysics );
+ pTerrainPhysics = physcollision->PolysoupCreate();
+ triCount = nTriCount;
+ }
+ Vector tmpVerts[3];
+ for ( int iTri = 0; iTri < nTriCount; ++iTri )
+ {
+ float flAlphaTotal = 0.0f;
+ for ( int iTriVert = 0; iTriVert < 3; ++iTriVert )
+ {
+ pDispInfo->GetVert( indices[iTri*3+iTriVert], tmpVerts[iTriVert] );
+ flAlphaTotal += pDispInfo->GetAlpha( indices[iTri*3+iTriVert] );
+ }
+
+ int nProp = g_SurfaceProperties[texinfo[pMapDisp->face.texinfo].texdata];
+ if ( flAlphaTotal > DISP_ALPHA_PROP_DELTA )
+ {
+ int nProp2 = GetSurfaceProperties2( matID, "surfaceprop2" );
+ if ( nProp2 != -1 )
+ {
+ nProp = nProp2;
+ }
+ }
+ int nMaterialIndex = RemapWorldMaterial( nProp );
+ physcollision->PolysoupAddTriangle( pTerrainPhysics, tmpVerts[0], tmpVerts[1], tmpVerts[2], nMaterialIndex );
+ }
+ }
+
+ // convert the whole grid's polysoup to a collide and store in the collision list
+ CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
+ if ( pCollide )
+ {
+ collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
+ }
+ // now that we have the collide, we're done with the soup
+ physcollision->PolysoupDestroy( pTerrainPhysics );
+ }
+}
+
+
+class CDispMeshEvent : public IVirtualMeshEvent
+{
+public:
+ CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo );
+ virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList );
+ virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs );
+ virtual void GetTrianglesInSphere( void *userData, const Vector ¢er, float radius, virtualmeshtrianglelist_t *pList );
+
+ CUtlVector<Vector> m_verts;
+ unsigned short *m_pIndices;
+ int m_indexCount;
+};
+
+CDispMeshEvent::CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo )
+{
+ m_pIndices = pIndices;
+ m_indexCount = indexCount;
+ int maxIndex = 0;
+ for ( int i = 0; i < indexCount; i++ )
+ {
+ if ( pIndices[i] > maxIndex )
+ {
+ maxIndex = pIndices[i];
+ }
+ }
+ for ( int i = 0; i < indexCount/2; i++ )
+ {
+ V_swap( pIndices[i], pIndices[(indexCount-i)-1] );
+ }
+ int count = maxIndex + 1;
+ m_verts.SetCount( count );
+ for ( int i = 0; i < count; i++ )
+ {
+ m_verts[i] = pDispInfo->GetVert(i);
+ }
+}
+
+void CDispMeshEvent::GetVirtualMesh( void *userData, virtualmeshlist_t *pList )
+{
+ Assert(userData==((void *)this));
+ pList->pVerts = m_verts.Base();
+ pList->indexCount = m_indexCount;
+ pList->triangleCount = m_indexCount/3;
+ pList->vertexCount = m_verts.Count();
+ pList->surfacePropsIndex = 0; // doesn't matter here, reset at runtime
+ pList->pHull = NULL;
+ int indexMax = ARRAYSIZE(pList->indices);
+ int indexCount = min(m_indexCount, indexMax);
+ Assert(m_indexCount < indexMax);
+ Q_memcpy( pList->indices, m_pIndices, sizeof(*m_pIndices) * indexCount );
+}
+
+void CDispMeshEvent::GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs )
+{
+ Assert(userData==((void *)this));
+ ClearBounds( *pMins, *pMaxs );
+ for ( int i = 0; i < m_verts.Count(); i++ )
+ {
+ AddPointToBounds( m_verts[i], *pMins, *pMaxs );
+ }
+}
+
+void CDispMeshEvent::GetTrianglesInSphere( void *userData, const Vector ¢er, float radius, virtualmeshtrianglelist_t *pList )
+{
+ Assert(userData==((void *)this));
+ pList->triangleCount = m_indexCount/3;
+ int indexMax = ARRAYSIZE(pList->triangleIndices);
+ int indexCount = min(m_indexCount, indexMax);
+ Assert(m_indexCount < MAX_VIRTUAL_TRIANGLES*3);
+ Q_memcpy( pList->triangleIndices, m_pIndices, sizeof(*m_pIndices) * indexCount );
+}
+
+void Disp_BuildVirtualMesh( int contentsMask )
+{
+ CUtlVector<CPhysCollide *> virtualMeshes;
+ virtualMeshes.EnsureCount( g_CoreDispInfos.Count() );
+ for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
+ {
+ CCoreDispInfo *pDispInfo = g_CoreDispInfos[ i ];
+ mapdispinfo_t *pMapDisp = &mapdispinfo[ i ];
+
+ virtualMeshes[i] = NULL;
+ // not solid for this pass
+ if ( !(pMapDisp->contents & contentsMask) )
+ continue;
+
+ // Build a triangle list. This shares the tesselation code with the engine.
+ CUtlVector<unsigned short> indices;
+ CVBSPTesselateHelper helper;
+ helper.m_pIndices = &indices;
+ helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
+ helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
+
+ ::TesselateDisplacement( &helper );
+
+ // validate the collision data
+ if ( 1 )
+ {
+ int triCount = indices.Count() / 3;
+ for ( int j = 0; j < triCount; j++ )
+ {
+ int index = j * 3;
+ Vector v0 = pDispInfo->GetVert( indices[index+0] );
+ Vector v1 = pDispInfo->GetVert( indices[index+1] );
+ Vector v2 = pDispInfo->GetVert( indices[index+2] );
+ if ( v0 == v1 || v1 == v2 || v2 == v0 )
+ {
+ Warning( "Displacement %d has bad geometry near %.2f %.2f %.2f\n", i, v0.x, v0.y, v0.z );
+ texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+
+ Error( "Can't compile displacement physics, exiting. Texture is %s\n", pMatName );
+ }
+ }
+
+ }
+ CDispMeshEvent meshHandler( indices.Base(), indices.Count(), pDispInfo );
+ virtualmeshparams_t params;
+ params.buildOuterHull = true;
+ params.pMeshEventHandler = &meshHandler;
+ params.userData = &meshHandler;
+ virtualMeshes[i] = physcollision->CreateVirtualMesh( params );
+ }
+ unsigned int totalSize = 0;
+ CUtlBuffer buf;
+ dphysdisp_t header;
+ header.numDisplacements = g_CoreDispInfos.Count();
+ buf.PutObjects( &header );
+
+ CUtlVector<char> dispBuf;
+ for ( int i = 0; i < header.numDisplacements; i++ )
+ {
+ if ( virtualMeshes[i] )
+ {
+ unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
+ totalSize += testSize;
+ buf.PutShort( testSize );
+ }
+ else
+ {
+ buf.PutShort( -1 );
+ }
+ }
+ for ( int i = 0; i < header.numDisplacements; i++ )
+ {
+ if ( virtualMeshes[i] )
+ {
+ unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
+ dispBuf.RemoveAll();
+ dispBuf.EnsureCount(testSize);
+
+ unsigned int outSize = physcollision->CollideWrite( dispBuf.Base(), virtualMeshes[i], false );
+ Assert( outSize == testSize );
+ buf.Put( dispBuf.Base(), outSize );
+ }
+ }
+ g_PhysDispSize = totalSize + sizeof(dphysdisp_t) + (sizeof(unsigned short) * header.numDisplacements);
+ Assert( buf.TellMaxPut() == g_PhysDispSize );
+ g_PhysDispSize = buf.TellMaxPut();
+ g_pPhysDisp = new byte[g_PhysDispSize];
+ Q_memcpy( g_pPhysDisp, buf.Base(), g_PhysDispSize );
+}
+
diff --git a/mp/src/utils/vbsp/disp_ivp.h b/mp/src/utils/vbsp/disp_ivp.h new file mode 100644 index 00000000..2c473f9a --- /dev/null +++ b/mp/src/utils/vbsp/disp_ivp.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DISP_IVP_H
+#define DISP_IVP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+#include "../../public/disp_tesselate.h"
+
+
+class CPhysCollisionEntry;
+struct dmodel_t;
+
+
+// This provides the template functions that the engine's tesselation code needs
+// so we can share the code in VBSP.
+class CVBSPTesselateHelper : public CBaseTesselateHelper
+{
+public:
+ void EndTriangle()
+ {
+ m_pIndices->AddToTail( m_TempIndices[0] );
+ m_pIndices->AddToTail( m_TempIndices[1] );
+ m_pIndices->AddToTail( m_TempIndices[2] );
+ }
+
+ DispNodeInfo_t& GetNodeInfo( int iNodeBit )
+ {
+ // VBSP doesn't care about these. Give it back something to play with.
+ static DispNodeInfo_t dummy;
+ return dummy;
+ }
+
+public:
+ CUtlVector<unsigned short> *m_pIndices;
+};
+
+
+extern void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask );
+extern void Disp_BuildVirtualMesh( int contentsMask );
+extern bool Disp_HasPower4Displacements();
+
+#endif // DISP_IVP_H
diff --git a/mp/src/utils/vbsp/disp_vbsp.cpp b/mp/src/utils/vbsp/disp_vbsp.cpp new file mode 100644 index 00000000..82934f67 --- /dev/null +++ b/mp/src/utils/vbsp/disp_vbsp.cpp @@ -0,0 +1,675 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "disp_vbsp.h"
+#include "tier0/dbg.h"
+#include "vbsp.h"
+#include "mstristrip.h"
+#include "writebsp.h"
+#include "pacifier.h"
+#include "disp_ivp.h"
+#include "builddisp.h"
+#include "mathlib/vector.h"
+
+// map displacement info -- runs parallel to the dispinfos struct
+int nummapdispinfo = 0;
+mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
+
+CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
+
+//-----------------------------------------------------------------------------
+// Computes the bounds for a disp info
+//-----------------------------------------------------------------------------
+void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs )
+{
+ CDispBox box;
+
+ // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
+ mapdispinfo_t *pMapDisp = &mapdispinfo[dispinfo];
+
+ CCoreDispInfo coreDispInfo;
+ DispMapToCoreDispInfo( pMapDisp, &coreDispInfo, NULL, NULL );
+
+ GetDispBox( &coreDispInfo, box );
+ mins = box.m_Min;
+ maxs = box.m_Max;
+}
+
+// Gets the barycentric coordinates of the position on the triangle where the lightmap
+// coordinates are equal to lmCoords. This always generates the coordinates but it
+// returns false if the point containing them does not lie inside the triangle.
+bool GetBarycentricCoordsFromLightmapCoords( Vector2D tri[3], Vector2D const &lmCoords, float bcCoords[3] )
+{
+ GetBarycentricCoords2D( tri[0], tri[1], tri[2], lmCoords, bcCoords );
+
+ return
+ (bcCoords[0] >= 0.0f && bcCoords[0] <= 1.0f) &&
+ (bcCoords[1] >= 0.0f && bcCoords[1] <= 1.0f) &&
+ (bcCoords[2] >= 0.0f && bcCoords[2] <= 1.0f);
+}
+
+
+bool FindTriIndexMapByUV( CCoreDispInfo *pCoreDisp, Vector2D const &lmCoords,
+ int &iTriangle, float flBarycentric[3] )
+{
+ const CPowerInfo *pPowerInfo = GetPowerInfo( pCoreDisp->GetPower() );
+
+ // Search all the triangles..
+ int nTriCount= pCoreDisp->GetTriCount();
+ for ( int iTri = 0; iTri < nTriCount; ++iTri )
+ {
+ unsigned short iVerts[3];
+// pCoreDisp->GetTriIndices( iTri, iVerts[0], iVerts[1], iVerts[2] );
+ CTriInfo *pTri = &pPowerInfo->m_pTriInfos[iTri];
+ iVerts[0] = pTri->m_Indices[0];
+ iVerts[1] = pTri->m_Indices[1];
+ iVerts[2] = pTri->m_Indices[2];
+
+ // Get this triangle's UVs.
+ Vector2D vecUV[3];
+ for ( int iCoord = 0; iCoord < 3; ++iCoord )
+ {
+ pCoreDisp->GetLuxelCoord( 0, iVerts[iCoord], vecUV[iCoord] );
+ }
+
+ // See if the passed-in UVs are in this triangle's UVs.
+ if( GetBarycentricCoordsFromLightmapCoords( vecUV, lmCoords, flBarycentric ) )
+ {
+ iTriangle = iTri;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void CalculateLightmapSamplePositions( CCoreDispInfo *pCoreDispInfo, const dface_t *pFace, CUtlVector<unsigned char> &out )
+{
+ int width = pFace->m_LightmapTextureSizeInLuxels[0] + 1;
+ int height = pFace->m_LightmapTextureSizeInLuxels[1] + 1;
+
+ // For each lightmap sample, find the triangle it sits in.
+ Vector2D lmCoords;
+ for( int y=0; y < height; y++ )
+ {
+ lmCoords.y = y + 0.5f;
+
+ for( int x=0; x < width; x++ )
+ {
+ lmCoords.x = x + 0.5f;
+
+ float flBarycentric[3];
+ int iTri;
+
+ if( FindTriIndexMapByUV( pCoreDispInfo, lmCoords, iTri, flBarycentric ) )
+ {
+ if( iTri < 255 )
+ {
+ out.AddToTail( iTri );
+ }
+ else
+ {
+ out.AddToTail( 255 );
+ out.AddToTail( iTri - 255 );
+ }
+
+ out.AddToTail( (unsigned char)( flBarycentric[0] * 255.9f ) );
+ out.AddToTail( (unsigned char)( flBarycentric[1] * 255.9f ) );
+ out.AddToTail( (unsigned char)( flBarycentric[2] * 255.9f ) );
+ }
+ else
+ {
+ out.AddToTail( 0 );
+ out.AddToTail( 0 );
+ out.AddToTail( 0 );
+ out.AddToTail( 0 );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int GetDispInfoEntityNum( mapdispinfo_t *pDisp )
+{
+ return pDisp->entitynum;
+}
+
+// Setup a CCoreDispInfo given a mapdispinfo_t.
+// If pFace is non-NULL, then lightmap texture coordinates will be generated.
+void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp, CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos )
+{
+ winding_t *pWinding = pMapDisp->face.originalface->winding;
+
+ Assert( pWinding->numpoints == 4 );
+
+ //
+ // set initial surface data
+ //
+ CCoreDispSurface *pSurf = pCoreDispInfo->GetSurface();
+
+ texinfo_t *pTexInfo = &texinfo[ pMapDisp->face.texinfo ];
+ Assert( pTexInfo != NULL );
+
+ // init material contents
+ pMapDisp->contents = pMapDisp->face.contents;
+ if (!(pMapDisp->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
+ {
+ pMapDisp->contents |= CONTENTS_SOLID;
+ }
+
+ pSurf->SetContents( pMapDisp->contents );
+
+ // Calculate the lightmap coordinates.
+ Vector2D tCoords[4] = {Vector2D(0,0),Vector2D(0,1),Vector2D(1,0),Vector2D(1,1)};
+ if( pFace )
+ {
+ Assert( pFace->numedges == 4 );
+
+ Vector pt[4];
+ for( int i=0; i < 4; i++ )
+ pt[i] = pWinding->p[i];
+
+ int zeroOffset[2] = {0,0};
+ CalcTextureCoordsAtPoints(
+ pTexInfo->textureVecsTexelsPerWorldUnits,
+ zeroOffset,
+ pt,
+ 4,
+ tCoords );
+ }
+
+ //
+ // set face point data ...
+ //
+ pSurf->SetPointCount( 4 );
+ for( int i = 0; i < 4; i++ )
+ {
+ // position
+ pSurf->SetPoint( i, pWinding->p[i] );
+ pSurf->SetTexCoord( i, tCoords[i] );
+ }
+
+ // reset surface given start info
+ pSurf->SetPointStart( pMapDisp->startPosition );
+ pSurf->FindSurfPointStartIndex();
+ pSurf->AdjustSurfPointData();
+
+ // Set the luxel coordinates on the base displacement surface.
+ Vector vecTmp( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
+ Vector vecU( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ Vector vecV( pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
+ bool bSwap = pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
+
+ // Set the face m_LightmapExtents
+ if ( pFace )
+ {
+ pFace->m_LightmapTextureSizeInLuxels[0] = pSurf->GetLuxelU();
+ pFace->m_LightmapTextureSizeInLuxels[1] = pSurf->GetLuxelV();
+ if ( bSwap )
+ {
+ if ( pSwappedTexInfos[ pMapDisp->face.texinfo ] < 0 )
+ {
+ // Create a new texinfo to hold the swapped data.
+ // We must do this because other surfaces may want the non-swapped data
+ // This fixes a lighting bug in d2_prison_08 where many non-displacement surfaces
+ // were pitch black, in addition to bugs in other maps I bet.
+
+ // NOTE: Copy here because adding a texinfo could realloc.
+ texinfo_t temp = *pTexInfo;
+ memcpy( temp.lightmapVecsLuxelsPerWorldUnits[0], pTexInfo->lightmapVecsLuxelsPerWorldUnits[1], 4 * sizeof(float) );
+ memcpy( temp.lightmapVecsLuxelsPerWorldUnits[1], pTexInfo->lightmapVecsLuxelsPerWorldUnits[0], 4 * sizeof(float) );
+ temp.lightmapVecsLuxelsPerWorldUnits[1][0] *= -1.0f;
+ temp.lightmapVecsLuxelsPerWorldUnits[1][1] *= -1.0f;
+ temp.lightmapVecsLuxelsPerWorldUnits[1][2] *= -1.0f;
+ temp.lightmapVecsLuxelsPerWorldUnits[1][3] *= -1.0f;
+ pSwappedTexInfos[ pMapDisp->face.texinfo ] = texinfo.AddToTail( temp );
+ }
+ pMapDisp->face.texinfo = pSwappedTexInfos[ pMapDisp->face.texinfo ];
+ }
+
+ // NOTE: This is here to help future-proof code, since there are codepaths where
+ // pTexInfo can be made invalid (texinfo.AddToTail above).
+ pTexInfo = NULL;
+ }
+
+ // Setup the displacement vectors and offsets.
+ int size = ( ( ( 1 << pMapDisp->power ) + 1 ) * ( ( 1 << pMapDisp->power ) + 1 ) );
+
+ Vector vectorDisps[2048];
+ float dispDists[2048];
+ Assert( size < sizeof(vectorDisps)/sizeof(vectorDisps[0]) );
+
+ for( int j = 0; j < size; j++ )
+ {
+ Vector v;
+ float dist;
+
+ VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
+ VectorAdd( v, pMapDisp->vectorOffsets[j], v );
+
+ dist = VectorLength( v );
+ VectorNormalize( v );
+
+ vectorDisps[j] = v;
+ dispDists[j] = dist;
+ }
+
+
+ // Use CCoreDispInfo to setup the actual vertex positions.
+ pCoreDispInfo->InitDispInfo( pMapDisp->power, pMapDisp->minTess, pMapDisp->smoothingAngle,
+ pMapDisp->alphaValues, vectorDisps, dispDists );
+ pCoreDispInfo->Create();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void EmitInitialDispInfos( void )
+{
+ int i;
+ mapdispinfo_t *pMapDisp;
+ ddispinfo_t *pDisp;
+ Vector v;
+
+ // Calculate the total number of verts.
+ int nTotalVerts = 0;
+ int nTotalTris = 0;
+ for ( i=0; i < nummapdispinfo; i++ )
+ {
+ nTotalVerts += NUM_DISP_POWER_VERTS( mapdispinfo[i].power );
+ nTotalTris += NUM_DISP_POWER_TRIS( mapdispinfo[i].power );
+ }
+
+ // Clear the output arrays..
+ g_dispinfo.Purge();
+ g_dispinfo.SetSize( nummapdispinfo );
+ g_DispVerts.SetSize( nTotalVerts );
+ g_DispTris.SetSize( nTotalTris );
+
+ int iCurVert = 0;
+ int iCurTri = 0;
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ pDisp = &g_dispinfo[i];
+ pMapDisp = &mapdispinfo[i];
+
+ CDispVert *pOutVerts = &g_DispVerts[iCurVert];
+ CDispTri *pOutTris = &g_DispTris[iCurTri];
+
+ // Setup the vert pointers.
+ pDisp->m_iDispVertStart = iCurVert;
+ pDisp->m_iDispTriStart = iCurTri;
+ iCurVert += NUM_DISP_POWER_VERTS( pMapDisp->power );
+ iCurTri += NUM_DISP_POWER_TRIS( pMapDisp->power );
+
+ //
+ // save power, minimum tesselation, and smoothing angle
+ //
+ pDisp->power = pMapDisp->power;
+
+ // If the high bit is set - this is FLAGS!
+ pDisp->minTess = pMapDisp->flags;
+ pDisp->minTess |= 0x80000000;
+// pDisp->minTess = pMapDisp->minTess;
+ pDisp->smoothingAngle = pMapDisp->smoothingAngle;
+ pDisp->m_iMapFace = (unsigned short)-2;
+
+ // get surface contents
+ pDisp->contents = pMapDisp->face.contents;
+
+ pDisp->startPosition = pMapDisp->startPosition;
+ //
+ // add up the vectorOffsets and displacements, save alphas (per vertex)
+ //
+ int size = ( ( ( 1 << pDisp->power ) + 1 ) * ( ( 1 << pDisp->power ) + 1 ) );
+ for( int j = 0; j < size; j++ )
+ {
+ VectorScale( pMapDisp->vectorDisps[j], pMapDisp->dispDists[j], v );
+ VectorAdd( v, pMapDisp->vectorOffsets[j], v );
+
+ float dist = VectorLength( v );
+ VectorNormalize( v );
+
+ VectorCopy( v, pOutVerts[j].m_vVector );
+ pOutVerts[j].m_flDist = dist;
+
+ pOutVerts[j].m_flAlpha = pMapDisp->alphaValues[j];
+ }
+
+ int nTriCount = ( (1 << (pDisp->power)) * (1 << (pDisp->power)) * 2 );
+ for ( int iTri = 0; iTri< nTriCount; ++iTri )
+ {
+ pOutTris[iTri].m_uiTags = pMapDisp->triTags[iTri];
+ }
+ //===================================================================
+ //===================================================================
+
+ // save the index for face data reference
+ pMapDisp->face.dispinfo = i;
+ }
+}
+
+
+void ExportCoreDispNeighborData( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
+{
+ for ( int i=0; i < 4; i++ )
+ {
+ pOut->m_EdgeNeighbors[i] = *pIn->GetEdgeNeighbor( i );
+ pOut->m_CornerNeighbors[i] = *pIn->GetCornerNeighbors( i );
+ }
+}
+
+void ExportNeighborData( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
+{
+ FindNeighboringDispSurfs( ppListBase, listSize );
+
+ // Export the neighbor data.
+ for ( int i=0; i < nummapdispinfo; i++ )
+ {
+ ExportCoreDispNeighborData( g_CoreDispInfos[i], &pBSPDispInfos[i] );
+ }
+}
+
+
+void ExportCoreDispAllowedVertList( const CCoreDispInfo *pIn, ddispinfo_t *pOut )
+{
+ ErrorIfNot(
+ pIn->GetAllowedVerts().GetNumDWords() == sizeof( pOut->m_AllowedVerts ) / 4,
+ ("ExportCoreDispAllowedVertList: size mismatch")
+ );
+ for ( int i=0; i < pIn->GetAllowedVerts().GetNumDWords(); i++ )
+ pOut->m_AllowedVerts[i] = pIn->GetAllowedVerts().GetDWord( i );
+}
+
+
+void ExportAllowedVertLists( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
+{
+ SetupAllowedVerts( ppListBase, listSize );
+
+ for ( int i=0; i < listSize; i++ )
+ {
+ ExportCoreDispAllowedVertList( ppListBase[i], &pBSPDispInfos[i] );
+ }
+}
+
+bool FindEnclosingTri(
+ const Vector2D &vert,
+ CUtlVector<Vector2D> &vertCoords,
+ CUtlVector<unsigned short> &indices,
+ int *pStartVert,
+ float bcCoords[3] )
+{
+ for ( int i=0; i < indices.Count(); i += 3 )
+ {
+ GetBarycentricCoords2D(
+ vertCoords[indices[i+0]],
+ vertCoords[indices[i+1]],
+ vertCoords[indices[i+2]],
+ vert,
+ bcCoords );
+
+ if ( bcCoords[0] >= 0 && bcCoords[0] <= 1 &&
+ bcCoords[1] >= 0 && bcCoords[1] <= 1 &&
+ bcCoords[2] >= 0 && bcCoords[2] <= 1 )
+ {
+ *pStartVert = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SnapRemainingVertsToSurface( CCoreDispInfo *pCoreDisp, ddispinfo_t *pDispInfo )
+{
+ // First, tesselate the displacement.
+ CUtlVector<unsigned short> indices;
+ CVBSPTesselateHelper helper;
+ helper.m_pIndices = &indices;
+ helper.m_pActiveVerts = pCoreDisp->GetAllowedVerts().Base();
+ helper.m_pPowerInfo = pCoreDisp->GetPowerInfo();
+ ::TesselateDisplacement( &helper );
+
+ // Figure out which verts are actually referenced in the tesselation.
+ CUtlVector<bool> vertsTouched;
+ vertsTouched.SetSize( pCoreDisp->GetSize() );
+ memset( vertsTouched.Base(), 0, sizeof( bool ) * vertsTouched.Count() );
+
+ for ( int i=0; i < indices.Count(); i++ )
+ vertsTouched[ indices[i] ] = true;
+
+ // Generate 2D floating point coordinates for each vertex. We use these to generate
+ // barycentric coordinates, and the scale doesn't matter.
+ CUtlVector<Vector2D> vertCoords;
+ vertCoords.SetSize( pCoreDisp->GetSize() );
+ for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
+ {
+ for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
+ vertCoords[y*pCoreDisp->GetWidth()+x].Init( x, y );
+ }
+
+ // Now, for each vert not touched, snap its position to the main surface.
+ for ( int y=0; y < pCoreDisp->GetHeight(); y++ )
+ {
+ for ( int x=0; x < pCoreDisp->GetWidth(); x++ )
+ {
+ int index = y * pCoreDisp->GetWidth() + x;
+ if ( !( vertsTouched[index] ) )
+ {
+ float bcCoords[3];
+ int iStartVert = -1;
+ if ( FindEnclosingTri( vertCoords[index], vertCoords, indices, &iStartVert, bcCoords ) )
+ {
+ const Vector &A = pCoreDisp->GetVert( indices[iStartVert+0] );
+ const Vector &B = pCoreDisp->GetVert( indices[iStartVert+1] );
+ const Vector &C = pCoreDisp->GetVert( indices[iStartVert+2] );
+ Vector vNewPos = A*bcCoords[0] + B*bcCoords[1] + C*bcCoords[2];
+
+ // This is kind of cheesy, but it gets the job done. Since the CDispVerts store the
+ // verts relative to some other offset, we'll just offset their position instead
+ // of setting it directly.
+ Vector vOffset = vNewPos - pCoreDisp->GetVert( index );
+
+ // Modify the mapfile vert.
+ CDispVert *pVert = &g_DispVerts[pDispInfo->m_iDispVertStart + index];
+ pVert->m_vVector = (pVert->m_vVector * pVert->m_flDist) + vOffset;
+ pVert->m_flDist = 1;
+
+ // Modify the CCoreDispInfo vert (although it probably won't be used later).
+ pCoreDisp->SetVert( index, vNewPos );
+ }
+ else
+ {
+ // This shouldn't happen because it would mean that the triangulation that
+ // disp_tesselation.h produced was missing a chunk of the space that the
+ // displacement covers.
+ // It also could indicate a floating-point epsilon error.. check to see if
+ // FindEnclosingTri finds a triangle that -almost- encloses the vert.
+ Assert( false );
+ }
+ }
+ }
+ }
+}
+
+void SnapRemainingVertsToSurface( CCoreDispInfo **ppListBase, ddispinfo_t *pBSPDispInfos, int listSize )
+{
+//g_pPad = ScratchPad3D_Create();
+ for ( int i=0; i < listSize; i++ )
+ {
+ SnapRemainingVertsToSurface( ppListBase[i], &pBSPDispInfos[i] );
+ }
+}
+
+void EmitDispLMAlphaAndNeighbors()
+{
+ int i;
+
+ Msg( "Finding displacement neighbors...\n" );
+
+ // Build the CCoreDispInfos.
+ CUtlVector<dface_t*> faces;
+
+ // Create the core dispinfos and init them for use as CDispUtilsHelpers.
+ for ( int iDisp = 0; iDisp < nummapdispinfo; ++iDisp )
+ {
+ CCoreDispInfo *pDisp = new CCoreDispInfo;
+ if ( !pDisp )
+ {
+ g_CoreDispInfos.Purge();
+ return;
+ }
+
+ int nIndex = g_CoreDispInfos.AddToTail();
+ pDisp->SetListIndex( nIndex );
+ g_CoreDispInfos[nIndex] = pDisp;
+ }
+
+ for ( i=0; i < nummapdispinfo; i++ )
+ {
+ g_CoreDispInfos[i]->SetDispUtilsHelperInfo( g_CoreDispInfos.Base(), nummapdispinfo );
+ }
+
+ faces.SetSize( nummapdispinfo );
+
+ int nMemSize = texinfo.Count() * sizeof(int);
+ int *pSwappedTexInfos = (int*)stackalloc( nMemSize );
+ memset( pSwappedTexInfos, 0xFF, nMemSize );
+ for( i = 0; i < numfaces; i++ )
+ {
+ dface_t *pFace = &dfaces[i];
+
+ if( pFace->dispinfo == -1 )
+ continue;
+
+ mapdispinfo_t *pMapDisp = &mapdispinfo[pFace->dispinfo];
+
+ // Set the displacement's face index.
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+ pDisp->m_iMapFace = i;
+
+ // Get a CCoreDispInfo. All we need is the triangles and lightmap texture coordinates.
+ CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[pFace->dispinfo];
+ DispMapToCoreDispInfo( pMapDisp, pCoreDispInfo, pFace, pSwappedTexInfos );
+
+ faces[pFace->dispinfo] = pFace;
+ }
+ stackfree( pSwappedTexInfos );
+
+ // Generate and export neighbor data.
+ ExportNeighborData( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
+
+ // Generate and export the active vert lists.
+ ExportAllowedVertLists( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
+
+
+ // Now that we know which vertices are actually going to be around, snap the ones that won't
+ // be around onto the slightly-reduced mesh. This is so the engine's ray test code and
+ // overlay code works right.
+ SnapRemainingVertsToSurface( g_CoreDispInfos.Base(), g_dispinfo.Base(), nummapdispinfo );
+
+ Msg( "Finding lightmap sample positions...\n" );
+ for ( i=0; i < nummapdispinfo; i++ )
+ {
+ dface_t *pFace = faces[i];
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+ CCoreDispInfo *pCoreDispInfo = g_CoreDispInfos[i];
+
+ pDisp->m_iLightmapSamplePositionStart = g_DispLightmapSamplePositions.Count();
+
+ CalculateLightmapSamplePositions( pCoreDispInfo, pFace, g_DispLightmapSamplePositions );
+ }
+
+ StartPacifier( "Displacement Alpha : ");
+
+ // Build lightmap alphas.
+ int dispCount = 0; // How many we've processed.
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ dface_t *pFace = faces[i];
+
+ Assert( pFace->dispinfo == i );
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+
+ // Allocate space for the alpha values.
+ pDisp->m_iLightmapAlphaStart = 0; // not used anymore
+
+ ++dispCount;
+ }
+
+ EndPacifier();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void DispGetFaceInfo( mapbrush_t *pBrush )
+{
+ int i;
+ side_t *pSide;
+
+ // we don't support displacement on entities at the moment!!
+ if( pBrush->entitynum != 0 )
+ {
+ char* pszEntityName = ValueForKey( &g_LoadingMap->entities[pBrush->entitynum], "classname" );
+ Error( "Error: displacement found on a(n) %s entity - not supported (entity %d, brush %d)\n", pszEntityName, pBrush->entitynum, pBrush->brushnum );
+ }
+
+ for( i = 0; i < pBrush->numsides; i++ )
+ {
+ pSide = &pBrush->original_sides[i];
+ if( pSide->pMapDisp )
+ {
+ // error checking!!
+ if( pSide->winding->numpoints != 4 )
+ Error( "Trying to create a non-quad displacement! (entity %d, brush %d)\n", pBrush->entitynum, pBrush->brushnum );
+ pSide->pMapDisp->face.originalface = pSide;
+ pSide->pMapDisp->face.texinfo = pSide->texinfo;
+ pSide->pMapDisp->face.dispinfo = -1;
+ pSide->pMapDisp->face.planenum = pSide->planenum;
+ pSide->pMapDisp->face.numpoints = pSide->winding->numpoints;
+ pSide->pMapDisp->face.w = CopyWinding( pSide->winding );
+ pSide->pMapDisp->face.contents = pBrush->contents;
+
+ pSide->pMapDisp->face.merged = FALSE;
+ pSide->pMapDisp->face.split[0] = FALSE;
+ pSide->pMapDisp->face.split[1] = FALSE;
+
+ pSide->pMapDisp->entitynum = pBrush->entitynum;
+ pSide->pMapDisp->brushSideID = pSide->id;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool HasDispInfo( mapbrush_t *pBrush )
+{
+ int i;
+ side_t *pSide;
+
+ for( i = 0; i < pBrush->numsides; i++ )
+ {
+ pSide = &pBrush->original_sides[i];
+ if( pSide->pMapDisp )
+ return true;
+ }
+
+ return false;
+}
diff --git a/mp/src/utils/vbsp/disp_vbsp.h b/mp/src/utils/vbsp/disp_vbsp.h new file mode 100644 index 00000000..1c26775e --- /dev/null +++ b/mp/src/utils/vbsp/disp_vbsp.h @@ -0,0 +1,46 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VBSP_DISPINFO_H
+#define VBSP_DISPINFO_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "vbsp.h"
+
+
+
+class CCoreDispInfo;
+
+
+extern CUtlVector<CCoreDispInfo*> g_CoreDispInfos;
+
+
+// Setup initial entries in g_dispinfo with some of the vertex data from the mapdisps.
+void EmitInitialDispInfos();
+
+// Resample vertex alpha into lightmap alpha for displacement surfaces so LOD popping artifacts are
+// less noticeable on the mid-to-high end.
+//
+// Also builds neighbor data.
+void EmitDispLMAlphaAndNeighbors();
+
+// Setup a CCoreDispInfo given a mapdispinfo_t.
+// If pFace is non-NULL, then lightmap texture coordinates will be generated.
+void DispMapToCoreDispInfo( mapdispinfo_t *pMapDisp,
+ CCoreDispInfo *pCoreDispInfo, dface_t *pFace, int *pSwappedTexInfos );
+
+
+void DispGetFaceInfo( mapbrush_t *pBrush );
+bool HasDispInfo( mapbrush_t *pBrush );
+
+// Computes the bounds for a disp info
+void ComputeDispInfoBounds( int dispinfo, Vector& mins, Vector& maxs );
+
+#endif // VBSP_DISPINFO_H
diff --git a/mp/src/utils/vbsp/faces.cpp b/mp/src/utils/vbsp/faces.cpp new file mode 100644 index 00000000..13ed9d61 --- /dev/null +++ b/mp/src/utils/vbsp/faces.cpp @@ -0,0 +1,1810 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// faces.c
+
+#include "vbsp.h"
+#include "utlvector.h"
+#include "utilmatlib.h"
+#include <float.h>
+#include "mstristrip.h"
+#include "tier1/strtools.h"
+#include "materialpatch.h"
+/*
+
+ some faces will be removed before saving, but still form nodes:
+
+ the insides of sky volumes
+ meeting planes of different water current volumes
+
+*/
+
+// undefine for dumb linear searches
+#define USE_HASHING
+
+#define INTEGRAL_EPSILON 0.01
+#define POINT_EPSILON 0.1
+#define OFF_EPSILON 0.25
+
+int c_merge;
+int c_subdivide;
+
+int c_totalverts;
+int c_uniqueverts;
+int c_degenerate;
+int c_tjunctions;
+int c_faceoverflows;
+int c_facecollapse;
+int c_badstartverts;
+
+#define MAX_SUPERVERTS 512
+int superverts[MAX_SUPERVERTS];
+int numsuperverts;
+
+face_t *edgefaces[MAX_MAP_EDGES][2];
+int firstmodeledge = 1;
+int firstmodelface;
+
+int c_tryedges;
+
+Vector edge_dir;
+Vector edge_start;
+vec_t edge_len;
+
+int num_edge_verts;
+int edge_verts[MAX_MAP_VERTS];
+
+
+float g_maxLightmapDimension = 32;
+
+
+face_t *NewFaceFromFace (face_t *f);
+
+// Used to speed up GetEdge2(). Holds a list of edges connected to each vert.
+CUtlVector<int> g_VertEdgeList[MAX_MAP_VERTS];
+
+
+//===========================================================================
+
+typedef struct hashvert_s
+{
+ struct hashvert_s *next;
+ int num;
+} hashvert_t;
+
+#define HASH_BITS 7
+#define HASH_SIZE (COORD_EXTENT>>HASH_BITS)
+
+
+int vertexchain[MAX_MAP_VERTS]; // the next vertex in a hash chain
+int hashverts[HASH_SIZE*HASH_SIZE]; // a vertex number, or 0 for no verts
+
+//face_t *edgefaces[MAX_MAP_EDGES][2];
+
+//============================================================================
+
+
+unsigned HashVec (Vector& vec)
+{
+ int x, y;
+
+ x = (MAX_COORD_INTEGER + (int)(vec[0]+0.5)) >> HASH_BITS;
+ y = (MAX_COORD_INTEGER + (int)(vec[1]+0.5)) >> HASH_BITS;
+
+ if ( x < 0 || x >= HASH_SIZE || y < 0 || y >= HASH_SIZE )
+ Error ("HashVec: point outside valid range");
+
+ return y*HASH_SIZE + x;
+}
+
+#ifdef USE_HASHING
+/*
+=============
+GetVertex
+
+Uses hashing
+=============
+*/
+int GetVertexnum (Vector& in)
+{
+ int h;
+ int i;
+ Vector vert;
+ int vnum;
+
+ c_totalverts++;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(in[i] - (int)(in[i]+0.5)) < INTEGRAL_EPSILON)
+ vert[i] = (int)(in[i]+0.5);
+ else
+ vert[i] = in[i];
+ }
+
+ h = HashVec (vert);
+
+ for (vnum=hashverts[h] ; vnum ; vnum=vertexchain[vnum])
+ {
+ Vector& p = dvertexes[vnum].point;
+ if ( fabs(p[0]-vert[0])<POINT_EPSILON
+ && fabs(p[1]-vert[1])<POINT_EPSILON
+ && fabs(p[2]-vert[2])<POINT_EPSILON )
+ return vnum;
+ }
+
+// emit a vertex
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
+
+ dvertexes[numvertexes].point[0] = vert[0];
+ dvertexes[numvertexes].point[1] = vert[1];
+ dvertexes[numvertexes].point[2] = vert[2];
+
+ vertexchain[numvertexes] = hashverts[h];
+ hashverts[h] = numvertexes;
+
+ c_uniqueverts++;
+
+ numvertexes++;
+
+ return numvertexes-1;
+}
+#else
+/*
+==================
+GetVertexnum
+
+Dumb linear search
+==================
+*/
+int GetVertexnum (Vector& v)
+{
+ int i, j;
+ dvertex_t *dv;
+ vec_t d;
+
+ c_totalverts++;
+
+ // make really close values exactly integral
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(v[i] - (int)(v[i]+0.5)) < INTEGRAL_EPSILON )
+ v[i] = (int)(v[i]+0.5);
+ if (v[i] < MIN_COORD_INTEGER || v[i] > MAX_COORD_INTEGER)
+ Error ("GetVertexnum: outside world, vertex %.1f %.1f %.1f", v.x, v.y, v.z);
+ }
+
+ // search for an existing vertex match
+ for (i=0, dv=dvertexes ; i<numvertexes ; i++, dv++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ d = v[j] - dv->point[j];
+ if ( d > POINT_EPSILON || d < -POINT_EPSILON)
+ break;
+ }
+ if (j == 3)
+ return i; // a match
+ }
+
+ // new point
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
+ VectorCopy (v, dv->point);
+ numvertexes++;
+ c_uniqueverts++;
+
+ return numvertexes-1;
+}
+#endif
+
+
+/*
+==================
+FaceFromSuperverts
+
+The faces vertexes have beeb added to the superverts[] array,
+and there may be more there than can be held in a face (MAXEDGES).
+
+If less, the faces vertexnums[] will be filled in, otherwise
+face will reference a tree of split[] faces until all of the
+vertexnums can be added.
+
+superverts[base] will become face->vertexnums[0], and the others
+will be circularly filled in.
+==================
+*/
+void FaceFromSuperverts (face_t **pListHead, face_t *f, int base)
+{
+ face_t *newf;
+ int remaining;
+ int i;
+
+ remaining = numsuperverts;
+ while (remaining > MAXEDGES)
+ { // must split into two faces, because of vertex overload
+ c_faceoverflows++;
+
+ newf = NewFaceFromFace (f);
+ f->split[0] = newf;
+
+ newf->next = *pListHead;
+ *pListHead = newf;
+
+ newf->numpoints = MAXEDGES;
+ for (i=0 ; i<MAXEDGES ; i++)
+ newf->vertexnums[i] = superverts[(i+base)%numsuperverts];
+
+ f->split[1] = NewFaceFromFace (f);
+ f = f->split[1];
+
+ f->next = *pListHead;
+ *pListHead = f;
+
+ remaining -= (MAXEDGES-2);
+ base = (base+MAXEDGES-1)%numsuperverts;
+ }
+
+ // copy the vertexes back to the face
+ f->numpoints = remaining;
+ for (i=0 ; i<remaining ; i++)
+ f->vertexnums[i] = superverts[(i+base)%numsuperverts];
+}
+
+
+/*
+==================
+EmitFaceVertexes
+==================
+*/
+void EmitFaceVertexes (face_t **pListHead, face_t *f)
+{
+ winding_t *w;
+ int i;
+
+ if (f->merged || f->split[0] || f->split[1])
+ return;
+
+ w = f->w;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ if (noweld)
+ { // make every point unique
+ if (numvertexes == MAX_MAP_VERTS)
+ Error ("Too many unique verts, max = %d (map has too much brush geometry)\n", MAX_MAP_VERTS);
+ superverts[i] = numvertexes;
+ VectorCopy (w->p[i], dvertexes[numvertexes].point);
+ numvertexes++;
+ c_uniqueverts++;
+ c_totalverts++;
+ }
+ else
+ superverts[i] = GetVertexnum (w->p[i]);
+ }
+ numsuperverts = w->numpoints;
+
+ // this may fragment the face if > MAXEDGES
+ FaceFromSuperverts (pListHead, f, 0);
+}
+
+/*
+==================
+EmitNodeFaceVertexes_r
+==================
+*/
+void EmitNodeFaceVertexes_r (node_t *node)
+{
+ int i;
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ // leaf faces are emitted in second pass
+ return;
+ }
+
+ for (f=node->faces ; f ; f=f->next)
+ {
+ EmitFaceVertexes (&node->faces, f);
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ EmitNodeFaceVertexes_r (node->children[i]);
+ }
+}
+
+void EmitLeafFaceVertexes( face_t **ppLeafFaceList )
+{
+ face_t *f = *ppLeafFaceList;
+
+ while ( f )
+ {
+ EmitFaceVertexes( ppLeafFaceList, f );
+ f = f->next;
+ }
+}
+
+
+#ifdef USE_HASHING
+/*
+==========
+FindEdgeVerts
+
+Uses the hash tables to cut down to a small number
+==========
+*/
+void FindEdgeVerts (Vector& v1, Vector& v2)
+{
+ int x1, x2, y1, y2, t;
+ int x, y;
+ int vnum;
+
+#if 0
+{
+ int i;
+ num_edge_verts = numvertexes-1;
+ for (i=0 ; i<numvertexes-1 ; i++)
+ edge_verts[i] = i+1;
+}
+#endif
+
+ x1 = (MAX_COORD_INTEGER + (int)(v1[0]+0.5)) >> HASH_BITS;
+ y1 = (MAX_COORD_INTEGER + (int)(v1[1]+0.5)) >> HASH_BITS;
+ x2 = (MAX_COORD_INTEGER + (int)(v2[0]+0.5)) >> HASH_BITS;
+ y2 = (MAX_COORD_INTEGER + (int)(v2[1]+0.5)) >> HASH_BITS;
+
+ if (x1 > x2)
+ {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if (y1 > y2)
+ {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+#if 0
+ x1--;
+ x2++;
+ y1--;
+ y2++;
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 >= HASH_SIZE)
+ x2 = HASH_SIZE;
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 >= HASH_SIZE)
+ y2 = HASH_SIZE;
+#endif
+ num_edge_verts = 0;
+ for (x=x1 ; x <= x2 ; x++)
+ {
+ for (y=y1 ; y <= y2 ; y++)
+ {
+ for (vnum=hashverts[y*HASH_SIZE+x] ; vnum ; vnum=vertexchain[vnum])
+ {
+ edge_verts[num_edge_verts++] = vnum;
+ }
+ }
+ }
+}
+
+#else
+/*
+==========
+FindEdgeVerts
+
+Forced a dumb check of everything
+==========
+*/
+void FindEdgeVerts (Vector& v1, Vector& v2)
+{
+ int i;
+
+ num_edge_verts = numvertexes-1;
+ for (i=0 ; i<num_edge_verts ; i++)
+ edge_verts[i] = i+1;
+}
+#endif
+
+/*
+==========
+TestEdge
+
+Can be recursively reentered
+==========
+*/
+void TestEdge (vec_t start, vec_t end, int p1, int p2, int startvert)
+{
+ int j, k;
+ vec_t dist;
+ Vector delta;
+ Vector exact;
+ Vector off;
+ vec_t error;
+ Vector p;
+
+ if (p1 == p2)
+ {
+ c_degenerate++;
+ return; // degenerate edge
+ }
+
+ for (k=startvert ; k<num_edge_verts ; k++)
+ {
+ j = edge_verts[k];
+ if (j==p1 || j == p2)
+ continue;
+
+ VectorCopy (dvertexes[j].point, p);
+
+ VectorSubtract (p, edge_start, delta);
+ dist = DotProduct (delta, edge_dir);
+ if (dist <=start || dist >= end)
+ continue; // off an end
+ VectorMA (edge_start, dist, edge_dir, exact);
+ VectorSubtract (p, exact, off);
+ error = off.Length();
+
+ if (error > OFF_EPSILON)
+ continue; // not on the edge
+
+ // break the edge
+ c_tjunctions++;
+ TestEdge (start, dist, p1, j, k+1);
+ TestEdge (dist, end, j, p2, k+1);
+ return;
+ }
+
+ // the edge p1 to p2 is now free of tjunctions
+ if (numsuperverts >= MAX_SUPERVERTS)
+ Error ("Edge with too many vertices due to t-junctions. Max %d verts along an edge!\n", MAX_SUPERVERTS);
+ superverts[numsuperverts] = p1;
+ numsuperverts++;
+}
+
+
+// stores the edges that each vert is part of
+struct face_vert_table_t
+{
+ face_vert_table_t()
+ {
+ edge0 = -1;
+ edge1 = -1;
+ }
+
+ void AddEdge( int edge )
+ {
+ if ( edge0 == -1 )
+ {
+ edge0 = edge;
+ }
+ else
+ {
+ // can only have two edges
+ Assert(edge1==-1);
+ edge1 = edge;
+ }
+ }
+
+ bool HasEdge( int edge ) const
+ {
+ if ( edge >= 0 )
+ {
+ if ( edge0 == edge || edge1 == edge )
+ return true;
+ }
+ return false;
+ }
+
+ int edge0;
+ int edge1;
+};
+
+// if these two verts share an edge, they must be collinear
+bool IsDiagonal( const face_vert_table_t &v0, const face_vert_table_t &v1 )
+{
+ if ( v1.HasEdge(v0.edge0) || v1.HasEdge(v0.edge1) )
+ return false;
+
+ return true;
+}
+
+
+void Triangulate_r( CUtlVector<int> &out, const CUtlVector<int> &inIndices, const CUtlVector<face_vert_table_t> &poly )
+{
+ Assert( inIndices.Count() > 2 );
+
+ // one triangle left, return
+ if ( inIndices.Count() == 3 )
+ {
+ for ( int i = 0; i < inIndices.Count(); i++ )
+ {
+ out.AddToTail( inIndices[i] );
+ }
+ return;
+ }
+
+ // check each pair of verts and see if they are diagonal (not on a shared edge)
+ // if so, split & recurse
+ for ( int i = 0; i < inIndices.Count(); i++ )
+ {
+ int count = inIndices.Count();
+
+ // i + count is myself, i + count-1 is previous, so we need to stop at i+count-2
+ for ( int j = 2; j < count-1; j++ )
+ {
+ // if these two form a diagonal, split the poly along
+ // the diagonal and triangulate the two sub-polys
+ int index = inIndices[i];
+ int nextArray = (i+j)%count;
+ int nextIndex = inIndices[nextArray];
+ if ( IsDiagonal(poly[index], poly[nextIndex]) )
+ {
+ // add the poly up to the diagonal
+ CUtlVector<int> in1;
+ for ( int k = i; k != nextArray; k = (k+1)%count )
+ {
+ in1.AddToTail(inIndices[k]);
+ }
+ in1.AddToTail(nextIndex);
+
+ // add the rest of the poly starting with the diagonal
+ CUtlVector<int> in2;
+ in2.AddToTail(index);
+ for ( int l = nextArray; l != i; l = (l+1)%count )
+ {
+ in2.AddToTail(inIndices[l]);
+ }
+
+ // triangulate the sub-polys
+ Triangulate_r( out, in1, poly );
+ Triangulate_r( out, in2, poly );
+ return;
+ }
+ }
+ }
+
+ // didn't find a diagonal
+ Assert(0);
+}
+
+/*
+==================
+FixFaceEdges
+
+==================
+*/
+void FixFaceEdges (face_t **pList, face_t *f)
+{
+ int p1, p2;
+ int i;
+ Vector e2;
+ vec_t len;
+ int count[MAX_SUPERVERTS], start[MAX_SUPERVERTS];
+ int base;
+
+ if (f->merged || f->split[0] || f->split[1])
+ return;
+
+ numsuperverts = 0;
+
+ int originalPoints = f->numpoints;
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ p1 = f->vertexnums[i];
+ p2 = f->vertexnums[(i+1)%f->numpoints];
+
+ VectorCopy (dvertexes[p1].point, edge_start);
+ VectorCopy (dvertexes[p2].point, e2);
+
+ FindEdgeVerts (edge_start, e2);
+
+ VectorSubtract (e2, edge_start, edge_dir);
+ len = VectorNormalize (edge_dir);
+
+ start[i] = numsuperverts;
+ TestEdge (0, len, p1, p2, 0);
+
+ count[i] = numsuperverts - start[i];
+ }
+
+ if (numsuperverts < 3)
+ { // entire face collapsed
+ f->numpoints = 0;
+ c_facecollapse++;
+ return;
+ }
+
+ // we want to pick a vertex that doesn't have tjunctions
+ // on either side, which can cause artifacts on trifans,
+ // especially underwater
+ for (i=0 ; i<f->numpoints ; i++)
+ {
+ if (count[i] == 1 && count[(i+f->numpoints-1)%f->numpoints] == 1)
+ break;
+ }
+ if (i == f->numpoints)
+ {
+ f->badstartvert = true;
+ c_badstartverts++;
+ base = 0;
+
+ }
+ else
+ { // rotate the vertex order
+ base = start[i];
+ }
+
+ // this may fragment the face if > MAXEDGES
+ FaceFromSuperverts (pList, f, base);
+
+ // if this is the world, then re-triangulate to sew cracks
+ if ( f->badstartvert && entity_num == 0 )
+ {
+ CUtlVector<face_vert_table_t> poly;
+ CUtlVector<int> inIndices;
+ CUtlVector<int> outIndices;
+ poly.AddMultipleToTail( numsuperverts );
+ for ( i = 0; i < originalPoints; i++ )
+ {
+ // edge may not have output any points. Don't mark
+ if ( !count[i] )
+ continue;
+ // mark each edge the point is a member of
+ // we'll use this as a fast "is collinear" test
+ for ( int j = 0; j <= count[i]; j++ )
+ {
+ int polyIndex = (start[i] + j) % numsuperverts;
+ poly[polyIndex].AddEdge( i );
+ }
+ }
+ for ( i = 0; i < numsuperverts; i++ )
+ {
+ inIndices.AddToTail( i );
+ }
+ Triangulate_r( outIndices, inIndices, poly );
+ dprimitive_t &newPrim = g_primitives[g_numprimitives];
+ f->firstPrimID = g_numprimitives;
+ g_numprimitives++;
+ f->numPrims = 1;
+ newPrim.firstIndex = g_numprimindices;
+ newPrim.firstVert = g_numprimverts;
+ newPrim.indexCount = outIndices.Count();
+ newPrim.vertCount = 0;
+ newPrim.type = PRIM_TRILIST;
+ g_numprimindices += newPrim.indexCount;
+ if ( g_numprimitives > MAX_MAP_PRIMITIVES || g_numprimindices > MAX_MAP_PRIMINDICES )
+ {
+ Error("Too many t-junctions to fix up! (%d prims, max %d :: %d indices, max %d)\n", g_numprimitives, MAX_MAP_PRIMITIVES, g_numprimindices, MAX_MAP_PRIMINDICES );
+ }
+ for ( i = 0; i < outIndices.Count(); i++ )
+ {
+ g_primindices[newPrim.firstIndex + i] = outIndices[i];
+ }
+ }
+}
+
+/*
+==================
+FixEdges_r
+==================
+*/
+void FixEdges_r (node_t *node)
+{
+ int i;
+ face_t *f;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ return;
+ }
+
+ for (f=node->faces ; f ; f=f->next)
+ FixFaceEdges (&node->faces, f);
+
+ for (i=0 ; i<2 ; i++)
+ FixEdges_r (node->children[i]);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fix the t-junctions on detail faces
+//-----------------------------------------------------------------------------
+void FixLeafFaceEdges( face_t **ppLeafFaceList )
+{
+ face_t *f;
+
+ for ( f = *ppLeafFaceList; f; f = f->next )
+ {
+ FixFaceEdges( ppLeafFaceList, f );
+ }
+}
+
+/*
+===========
+FixTjuncs
+
+===========
+*/
+
+face_t *FixTjuncs (node_t *headnode, face_t *pLeafFaceList)
+{
+ // snap and merge all vertexes
+ qprintf ("---- snap verts ----\n");
+ memset (hashverts, 0, sizeof(hashverts));
+ memset (vertexchain, 0, sizeof(vertexchain));
+ c_totalverts = 0;
+ c_uniqueverts = 0;
+ c_faceoverflows = 0;
+ EmitNodeFaceVertexes_r (headnode);
+
+ // UNDONE: This count is wrong with tjuncs off on details - since
+
+ // break edges on tjunctions
+ qprintf ("---- tjunc ----\n");
+ c_tryedges = 0;
+ c_degenerate = 0;
+ c_facecollapse = 0;
+ c_tjunctions = 0;
+
+ if ( g_bAllowDetailCracks )
+ {
+ FixEdges_r (headnode);
+ EmitLeafFaceVertexes( &pLeafFaceList );
+ FixLeafFaceEdges( &pLeafFaceList );
+ }
+ else
+ {
+ EmitLeafFaceVertexes( &pLeafFaceList );
+ if (!notjunc)
+ {
+ FixEdges_r (headnode);
+ FixLeafFaceEdges( &pLeafFaceList );
+ }
+ }
+
+
+ qprintf ("%i unique from %i\n", c_uniqueverts, c_totalverts);
+ qprintf ("%5i edges degenerated\n", c_degenerate);
+ qprintf ("%5i faces degenerated\n", c_facecollapse);
+ qprintf ("%5i edges added by tjunctions\n", c_tjunctions);
+ qprintf ("%5i faces added by tjunctions\n", c_faceoverflows);
+ qprintf ("%5i bad start verts\n", c_badstartverts);
+
+ return pLeafFaceList;
+}
+
+
+//========================================================
+
+int c_faces;
+
+face_t *AllocFace (void)
+{
+ static int s_FaceId = 0;
+
+ face_t *f;
+
+ f = (face_t*)malloc(sizeof(*f));
+ memset (f, 0, sizeof(*f));
+ f->id = s_FaceId;
+ ++s_FaceId;
+
+ c_faces++;
+
+ return f;
+}
+
+face_t *NewFaceFromFace (face_t *f)
+{
+ face_t *newf;
+
+ newf = AllocFace ();
+ *newf = *f;
+ newf->merged = NULL;
+ newf->split[0] = newf->split[1] = NULL;
+ newf->w = NULL;
+ return newf;
+}
+
+void FreeFace (face_t *f)
+{
+ if (f->w)
+ FreeWinding (f->w);
+ free (f);
+ c_faces--;
+}
+
+
+void FreeFaceList( face_t *pFaces )
+{
+ while ( pFaces )
+ {
+ face_t *next = pFaces->next;
+
+ FreeFace( pFaces );
+ pFaces = next;
+ }
+}
+
+//========================================================
+
+void GetEdge2_InitOptimizedList()
+{
+ for( int i=0; i < MAX_MAP_VERTS; i++ )
+ g_VertEdgeList[i].RemoveAll();
+}
+
+
+void IntSort( CUtlVector<int> &theList )
+{
+ for( int i=0; i < theList.Size()-1; i++ )
+ {
+ if( theList[i] > theList[i+1] )
+ {
+ int temp = theList[i];
+ theList[i] = theList[i+1];
+ theList[i+1] = temp;
+ if( i > 0 )
+ i -= 2;
+ else
+ i = -1;
+ }
+ }
+}
+
+
+int AddEdge( int v1, int v2, face_t *f )
+{
+ if (numedges >= MAX_MAP_EDGES)
+ Error ("Too many edges in map, max == %d", MAX_MAP_EDGES);
+
+ g_VertEdgeList[v1].AddToTail( numedges );
+ g_VertEdgeList[v2].AddToTail( numedges );
+ IntSort( g_VertEdgeList[v1] );
+ IntSort( g_VertEdgeList[v2] );
+
+ dedge_t *edge = &dedges[numedges];
+ numedges++;
+
+ edge->v[0] = v1;
+ edge->v[1] = v2;
+ edgefaces[numedges-1][0] = f;
+ return numedges - 1;
+}
+
+
+/*
+==================
+GetEdge
+
+Called by writebsp.
+Don't allow four way edges
+==================
+*/
+int GetEdge2 (int v1, int v2, face_t *f)
+{
+ dedge_t *edge;
+
+ c_tryedges++;
+
+ if (!noshare)
+ {
+ // Check all edges connected to v1.
+ CUtlVector<int> &theList = g_VertEdgeList[v1];
+ for( int i=0; i < theList.Size(); i++ )
+ {
+ int iEdge = theList[i];
+ edge = &dedges[iEdge];
+ if (v1 == edge->v[1] && v2 == edge->v[0] && edgefaces[iEdge][0]->contents == f->contents)
+ {
+ if (edgefaces[iEdge][1])
+ continue;
+
+ edgefaces[iEdge][1] = f;
+ return -iEdge;
+ }
+ }
+ }
+
+ return AddEdge( v1, v2, f );
+}
+
+/*
+===========================================================================
+
+FACE MERGING
+
+===========================================================================
+*/
+
+#define CONTINUOUS_EPSILON 0.001
+
+/*
+=============
+TryMergeWinding
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, Vector& planenormal)
+{
+ Vector *p1, *p2, *p3, *p4, *back;
+ winding_t *newf;
+ int i, j, k, l;
+ Vector normal, delta;
+ vec_t dot;
+ qboolean keep1, keep2;
+
+
+ //
+ // find a common edge
+ //
+ p1 = p2 = NULL; // stop compiler warning
+ j = 0; //
+
+ for (i=0 ; i<f1->numpoints ; i++)
+ {
+ p1 = &f1->p[i];
+ p2 = &f1->p[(i+1)%f1->numpoints];
+ for (j=0 ; j<f2->numpoints ; j++)
+ {
+ p3 = &f2->p[j];
+ p4 = &f2->p[(j+1)%f2->numpoints];
+ for (k=0 ; k<3 ; k++)
+ {
+ if (fabs((*p1)[k] - (*p4)[k]) > EQUAL_EPSILON)
+ break;
+ if (fabs((*p2)[k] - (*p3)[k]) > EQUAL_EPSILON)
+ break;
+ }
+ if (k==3)
+ break;
+ }
+ if (j < f2->numpoints)
+ break;
+ }
+
+ if (i == f1->numpoints)
+ return NULL; // no matching edges
+
+ //
+ // check slope of connected lines
+ // if the slopes are colinear, the point can be removed
+ //
+ back = &f1->p[(i+f1->numpoints-1)%f1->numpoints];
+ VectorSubtract (*p1, *back, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal);
+
+ back = &f2->p[(j+2)%f2->numpoints];
+ VectorSubtract (*back, *p1, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ back = &f1->p[(i+2)%f1->numpoints];
+ VectorSubtract (*back, *p2, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal);
+
+ back = &f2->p[(j+f2->numpoints-1)%f2->numpoints];
+ VectorSubtract (*back, *p2, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ //
+ // build the new polygon
+ //
+ newf = AllocWinding (f1->numpoints + f2->numpoints);
+
+ // copy first polygon
+ for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
+ {
+ if (k==(i+1)%f1->numpoints && !keep2)
+ continue;
+
+ VectorCopy (f1->p[k], newf->p[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ // copy second polygon
+ for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
+ {
+ if (l==(j+1)%f2->numpoints && !keep1)
+ continue;
+ VectorCopy (f2->p[l], newf->p[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ return newf;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool OverlaysAreEqual( face_t *f1, face_t *f2 )
+{
+ // Check the overlay ids - see if they are the same.
+ if ( f1->originalface->aOverlayIds.Count() != f2->originalface->aOverlayIds.Count() )
+ return false;
+
+ int nOverlayCount = f1->originalface->aOverlayIds.Count();
+ for ( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ int nOverlayId = f1->originalface->aOverlayIds[iOverlay];
+ if ( f2->originalface->aOverlayIds.Find( nOverlayId ) == -1 )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool FaceOnWaterBrush( face_t *face )
+{
+ side_t *pSide = face->originalface;
+ if ( !pSide )
+ return false;
+
+ if ( pSide->contents & ( CONTENTS_WATER | CONTENTS_SLIME ) )
+ return true;
+
+ return false;
+}
+
+/*
+=============
+TryMerge
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+face_t *TryMerge (face_t *f1, face_t *f2, Vector& planenormal)
+{
+ face_t *newf;
+ winding_t *nw;
+
+ if (!f1->w || !f2->w)
+ return NULL;
+ if (f1->texinfo != f2->texinfo)
+ return NULL;
+ if (f1->planenum != f2->planenum) // on front and back sides
+ return NULL;
+ if (f1->contents != f2->contents)
+ return NULL;
+ if ( f1->originalface->smoothingGroups != f2->originalface->smoothingGroups )
+ return NULL;
+ if ( !OverlaysAreEqual( f1, f2 ) )
+ return NULL;
+ if ( nomergewater && ( FaceOnWaterBrush( f1 ) || FaceOnWaterBrush( f2 ) ) )
+ return NULL;
+
+ nw = TryMergeWinding (f1->w, f2->w, planenormal);
+ if (!nw)
+ return NULL;
+
+ c_merge++;
+ newf = NewFaceFromFace (f1);
+ newf->w = nw;
+
+ f1->merged = newf;
+ f2->merged = newf;
+
+ return newf;
+}
+
+/*
+===============
+MergeFaceList
+===============
+*/
+void MergeFaceList(face_t **pList)
+{
+ face_t *f1, *f2, *end;
+ face_t *merged;
+ plane_t *plane;
+
+ merged = NULL;
+
+ for (f1 = *pList; f1 ; f1 = f1->next)
+ {
+ if (f1->merged || f1->split[0] || f1->split[1])
+ continue;
+ for (f2 = *pList; f2 != f1 ; f2=f2->next)
+ {
+ if (f2->merged || f2->split[0] || f2->split[1])
+ continue;
+
+ plane = &g_MainMap->mapplanes[f1->planenum];
+ merged = TryMerge (f1, f2, plane->normal);
+ if (!merged)
+ continue;
+
+ // add merged to the end of the face list
+ // so it will be checked against all the faces again
+ for (end = *pList; end->next ; end = end->next)
+ ;
+ merged->next = NULL;
+ end->next = merged;
+ break;
+ }
+ }
+}
+
+//=====================================================================
+
+/*
+===============
+SubdivideFace
+
+Chop up faces that are larger than we want in the surface cache
+===============
+*/
+void SubdivideFace (face_t **pFaceList, face_t *f)
+{
+ float mins, maxs;
+ vec_t v;
+ vec_t luxelsPerWorldUnit;
+ int axis, i;
+ texinfo_t *tex;
+ Vector temp;
+ vec_t dist;
+ winding_t *w, *frontw, *backw;
+
+ if ( f->merged || f->split[0] || f->split[1] )
+ return;
+
+// special (non-surface cached) faces don't need subdivision
+ tex = &texinfo[f->texinfo];
+
+ if( tex->flags & SURF_NOLIGHT )
+ {
+ return;
+ }
+
+ for (axis = 0 ; axis < 2 ; axis++)
+ {
+ while (1)
+ {
+ mins = 999999;
+ maxs = -999999;
+
+ VECTOR_COPY (tex->lightmapVecsLuxelsPerWorldUnits[axis], temp);
+ w = f->w;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ v = DotProduct (w->p[i], temp);
+ if (v < mins)
+ mins = v;
+ if (v > maxs)
+ maxs = v;
+ }
+#if 0
+ if (maxs - mins <= 0)
+ Error ("zero extents");
+#endif
+ if (maxs - mins <= g_maxLightmapDimension)
+ break;
+
+ // split it
+ c_subdivide++;
+
+ luxelsPerWorldUnit = VectorNormalize (temp);
+
+ dist = ( mins + g_maxLightmapDimension - 1 ) / luxelsPerWorldUnit;
+
+ ClipWindingEpsilon (w, temp, dist, ON_EPSILON, &frontw, &backw);
+ if (!frontw || !backw)
+ Error ("SubdivideFace: didn't split the polygon");
+
+ f->split[0] = NewFaceFromFace (f);
+ f->split[0]->w = frontw;
+ f->split[0]->next = *pFaceList;
+ *pFaceList = f->split[0];
+
+ f->split[1] = NewFaceFromFace (f);
+ f->split[1]->w = backw;
+ f->split[1]->next = *pFaceList;
+ *pFaceList = f->split[1];
+
+ SubdivideFace (pFaceList, f->split[0]);
+ SubdivideFace (pFaceList, f->split[1]);
+ return;
+ }
+ }
+}
+
+void SubdivideFaceList(face_t **pFaceList)
+{
+ face_t *f;
+
+ for (f = *pFaceList ; f ; f=f->next)
+ {
+ SubdivideFace (pFaceList, f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Assigns the bottom material to the bottom face
+//-----------------------------------------------------------------------------
+static bool AssignBottomWaterMaterialToFace( face_t *f )
+{
+ // NOTE: This happens *after* cubemap fixup occurs, so we need to get the
+ // fixed-up bottom material for this
+ texinfo_t *pTexInfo = &texinfo[f->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+
+ char pBottomMatName[512];
+ if ( !GetValueFromPatchedMaterial( pMaterialName, "$bottommaterial", pBottomMatName, 512 ) )
+ {
+ if( !Q_stristr( pMaterialName, "nodraw" ) && !Q_stristr( pMaterialName, "toolsskip" ) )
+ {
+ Warning("error: material %s doesn't have a $bottommaterial\n", pMaterialName );
+ }
+ return false;
+ }
+
+ //Assert( mapplanes[f->planenum].normal.z < 0 );
+ texinfo_t newTexInfo;
+ newTexInfo.flags = pTexInfo->flags;
+ int j, k;
+ for (j=0 ; j<2 ; j++)
+ {
+ for (k=0 ; k<4 ; k++)
+ {
+ newTexInfo.textureVecsTexelsPerWorldUnits[j][k] = pTexInfo->textureVecsTexelsPerWorldUnits[j][k];
+ newTexInfo.lightmapVecsLuxelsPerWorldUnits[j][k] = pTexInfo->lightmapVecsLuxelsPerWorldUnits[j][k];
+ }
+ }
+ newTexInfo.texdata = FindOrCreateTexData( pBottomMatName );
+ f->texinfo = FindOrCreateTexInfo( newTexInfo );
+
+ return true;
+}
+
+
+//===========================================================================
+
+int c_nodefaces;
+
+static void SubdivideFaceBySubdivSize( face_t *f, float subdivsize );
+void SubdivideFaceBySubdivSize( face_t *f );
+
+/*
+============
+FaceFromPortal
+
+============
+*/
+extern int FindOrCreateTexInfo( const texinfo_t &searchTexInfo );
+
+face_t *FaceFromPortal (portal_t *p, int pside)
+{
+ face_t *f;
+ side_t *side;
+ int deltaContents;
+
+ // portal does not bridge different visible contents
+ side = p->side;
+ if (!side)
+ return NULL;
+
+ // allocate a new face
+ f = AllocFace();
+
+ // save the original "side" from the map brush -- portal->side
+ // see FindPortalSide(...)
+ f->originalface = side;
+
+ //
+ // save material info
+ //
+ f->texinfo = side->texinfo;
+ f->dispinfo = -1; // all faces with displacement info are created elsewhere
+ f->smoothingGroups = side->smoothingGroups;
+
+ // save plane info
+ f->planenum = (side->planenum & ~1) | pside;
+ if ( entity_num != 0 )
+ {
+ // the brush model renderer doesn't use PLANEBACK, so write the real plane
+ // inside water faces can be flipped because they are generated on the inside of the brush
+ if ( p->nodes[pside]->contents & (CONTENTS_WATER|CONTENTS_SLIME) )
+ {
+ f->planenum = (side->planenum & ~1) | pside;
+ }
+ else
+ {
+ f->planenum = side->planenum;
+ }
+ }
+
+ // save portal info
+ f->portal = p;
+ f->fogVolumeLeaf = NULL;
+
+ deltaContents = VisibleContents(p->nodes[!pside]->contents^p->nodes[pside]->contents);
+
+ // don't show insides of windows or grates
+ if ( ((p->nodes[pside]->contents & CONTENTS_WINDOW) && deltaContents == CONTENTS_WINDOW) ||
+ ((p->nodes[pside]->contents & CONTENTS_GRATE) && deltaContents == CONTENTS_GRATE) )
+ {
+ FreeFace( f );
+ return NULL;
+ }
+
+ if ( p->nodes[pside]->contents & MASK_WATER )
+ {
+ f->fogVolumeLeaf = p->nodes[pside];
+ }
+ else if ( p->nodes[!pside]->contents & MASK_WATER )
+ {
+ f->fogVolumeLeaf = p->nodes[!pside];
+ }
+
+ // If it's the underside of water, we need to figure out what material to use, etc.
+ if( ( p->nodes[pside]->contents & CONTENTS_WATER ) && deltaContents == CONTENTS_WATER )
+ {
+ if ( !AssignBottomWaterMaterialToFace( f ) )
+ {
+ FreeFace( f );
+ return NULL;
+ }
+ }
+
+ //
+ // generate the winding for the face and save face contents
+ //
+ if( pside )
+ {
+ f->w = ReverseWinding(p->winding);
+ f->contents = p->nodes[1]->contents;
+ }
+ else
+ {
+ f->w = CopyWinding(p->winding);
+ f->contents = p->nodes[0]->contents;
+ }
+
+ f->numPrims = 0;
+ f->firstPrimID = 0;
+
+ // return the created face
+ return f;
+}
+
+/*
+===============
+MakeFaces_r
+
+If a portal will make a visible face,
+mark the side that originally created it
+
+ solid / empty : solid
+ solid / water : solid
+ water / empty : water
+ water / water : none
+===============
+*/
+void MakeFaces_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+
+ // recurse down to leafs
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ MakeFaces_r (node->children[0]);
+ MakeFaces_r (node->children[1]);
+
+ // merge together all visible faces on the node
+ if (!nomerge)
+ MergeFaceList(&node->faces);
+ if (!nosubdiv)
+ SubdivideFaceList(&node->faces);
+
+ return;
+ }
+
+ // solid leafs never have visible faces
+ if (node->contents & CONTENTS_SOLID)
+ return;
+
+ // see which portals are valid
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ p->face[s] = FaceFromPortal (p, s);
+ if (p->face[s])
+ {
+ c_nodefaces++;
+ p->face[s]->next = p->onnode->faces;
+ p->onnode->faces = p->face[s];
+ }
+ }
+}
+
+typedef winding_t *pwinding_t;
+
+static void PrintWinding( winding_t *w )
+{
+ int i;
+ Msg( "\t---\n" );
+ for( i = 0; i < w->numpoints; i++ )
+ {
+ Msg( "\t%f %f %f\n", w->p[i].x, w->p[i].y, w->p[i].z );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a winding to the current list of primverts
+// Input : *w - the winding
+// *pIndices - The output indices
+// vertStart - the starting vert index
+// vertCount - current count
+// Output : int - output count including new verts from this winding
+//-----------------------------------------------------------------------------
+int AddWindingToPrimverts( const winding_t *w, unsigned short *pIndices, int vertStart, int vertCount )
+{
+ for( int i = 0; i < w->numpoints; i++ )
+ {
+ int j;
+ for( j = vertStart; j < vertStart + vertCount; j++ )
+ {
+ Vector tmp = g_primverts[j].pos - w->p[i];
+
+ if( tmp.LengthSqr() < POINT_EPSILON*POINT_EPSILON )
+ {
+ pIndices[i] = j;
+ break;
+ }
+ }
+ if ( j >= vertStart + vertCount )
+ {
+ pIndices[i] = j;
+ g_primverts[j].pos = w->p[i];
+ vertCount++;
+ g_numprimverts++;
+ if ( g_numprimverts > MAX_MAP_PRIMVERTS )
+ {
+ Error( "Exceeded max water verts.\nIncrease surface subdivision size or lower your subdivision size in vmt files! (%d>%d)\n",
+ ( int )g_numprimverts, ( int )MAX_MAP_PRIMVERTS );
+ }
+ }
+ }
+
+ return vertCount;
+}
+
+
+
+#pragma optimize( "g", off )
+#define USE_TRISTRIPS
+
+// UNDONE: Should split this function into subdivide and primitive building parts
+// UNDONE: We should try building strips of shared verts for all water faces in a leaf
+// since those will be drawn concurrently anyway. It should be more efficient.
+static void SubdivideFaceBySubdivSize( face_t *f, float subdivsize )
+{
+ // garymcthack - REFACTOR ME!!!
+
+ vec_t dummy;
+ Vector hackNormal;
+ WindingPlane( f->w, hackNormal, &dummy );
+
+ // HACK - only subdivide stuff that is facing up or down (for water)
+ if( fabs(hackNormal[2]) < .9f )
+ {
+ return;
+ }
+
+ // Get the extents of the surface.
+ // garymcthack - this assumes a surface of constant z for now (for water). . can generalize later.
+ subdivsize = ( int )subdivsize;
+ winding_t *w;
+ w = CopyWinding( f->w );
+
+ Vector min, max;
+ WindingBounds( w, min, max );
+
+#if 0
+ Msg( "START WINDING: \n" );
+ PrintWinding( w );
+#endif
+ int xStart, yStart, xEnd, yEnd, xSteps, ySteps;
+ xStart = ( int )subdivsize * ( int )( ( min[0] - subdivsize ) / subdivsize );
+ xEnd = ( int )subdivsize * ( int )( ( max[0] + subdivsize ) / subdivsize );
+ yStart = ( int )subdivsize * ( int )( ( min[1] - subdivsize ) / subdivsize );
+ yEnd = ( int )subdivsize * ( int )( ( max[1] + subdivsize ) / subdivsize );
+ xSteps = ( xEnd - xStart ) / subdivsize;
+ ySteps = ( yEnd - yStart ) / subdivsize;
+ int x, y;
+ int xi, yi;
+ winding_t **windings = ( winding_t ** )new pwinding_t[xSteps * ySteps];
+ memset( windings, 0, sizeof( winding_t * ) * xSteps * ySteps );
+
+ for( yi = 0, y = yStart; y < yEnd; y += ( int )subdivsize, yi++ )
+ {
+ for( xi = 0, x = xStart; x < xEnd; x += ( int )subdivsize, xi++ )
+ {
+ winding_t *tempWinding, *frontWinding, *backWinding;
+ float planeDist;
+ Vector normal;
+ normal.Init( 1.0f, 0.0f, 0.0f );
+ planeDist = ( float )x;
+ tempWinding = CopyWinding( w );
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+ tempWinding = frontWinding;
+
+ normal.Init( -1.0f, 0.0f, 0.0f );
+ planeDist = -( float )( x + subdivsize );
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+ tempWinding = frontWinding;
+
+ normal.Init( 0.0f, 1.0f, 0.0f );
+ planeDist = ( float )y;
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+ tempWinding = frontWinding;
+
+ normal.Init( 0.0f, -1.0f, 0.0f );
+ planeDist = -( float )( y + subdivsize );
+ ClipWindingEpsilon( tempWinding, normal, planeDist, ON_EPSILON,
+ &frontWinding, &backWinding );
+ if( tempWinding )
+ {
+ FreeWinding( tempWinding );
+ }
+ if( backWinding )
+ {
+ FreeWinding( backWinding );
+ }
+ if( !frontWinding )
+ {
+ continue;
+ }
+
+#if 0
+ Msg( "output winding:\n" );
+ PrintWinding( frontWinding );
+#endif
+
+ if( frontWinding )
+ {
+ windings[xi + yi * xSteps] = frontWinding;
+ }
+ }
+ }
+ FreeWinding( w );
+ dprimitive_t &newPrim = g_primitives[g_numprimitives];
+ f->firstPrimID = g_numprimitives;
+ f->numPrims = 1;
+ newPrim.firstIndex = g_numprimindices;
+ newPrim.firstVert = g_numprimverts;
+ newPrim.indexCount = 0;
+ newPrim.vertCount = 0;
+#ifdef USE_TRISTRIPS
+ newPrim.type = PRIM_TRISTRIP;
+#else
+ newPrim.type = PRIM_TRILIST;
+#endif
+
+ CUtlVector<WORD> triListIndices;
+ int i;
+ for( i = 0; i < xSteps * ySteps; i++ )
+ {
+ if( !windings[i] )
+ {
+ continue;
+ }
+ unsigned short *pIndices =
+ ( unsigned short * )_alloca( windings[i]->numpoints * sizeof( unsigned short ) );
+ // find indices for the verts.
+ newPrim.vertCount = AddWindingToPrimverts( windings[i], pIndices, newPrim.firstVert, newPrim.vertCount );
+
+ // Now that we have indices for the verts, fan-tesselate the polygon and spit out tris.
+ for( int j = 0; j < windings[i]->numpoints - 2; j++ )
+ {
+ triListIndices.AddToTail( pIndices[0] );
+ triListIndices.AddToTail( pIndices[j+1] );
+ triListIndices.AddToTail( pIndices[j+2] );
+ }
+ }
+
+ delete [] windings;
+ // We've already updated the verts and have a trilist. . let's strip it!
+ if( !triListIndices.Size() )
+ {
+ return;
+ }
+
+#ifdef USE_TRISTRIPS
+ int numTristripIndices;
+ WORD *pStripIndices = NULL;
+ Stripify( triListIndices.Size() / 3, triListIndices.Base(), &numTristripIndices,
+ &pStripIndices );
+ Assert( pStripIndices );
+
+ // FIXME: Should also call ComputeVertexPermutation and reorder the verts.
+
+ for( i = 0; i < numTristripIndices; i++ )
+ {
+ Assert( pStripIndices[i] >= newPrim.firstVert &&
+ pStripIndices[i] < newPrim.firstVert + newPrim.vertCount );
+ g_primindices[newPrim.firstIndex + newPrim.indexCount] = pStripIndices[i];
+ newPrim.indexCount++;
+ g_numprimindices++;
+ if( g_numprimindices > MAX_MAP_PRIMINDICES )
+ {
+ Error( "Exceeded max water indicies.\nIncrease surface subdivision size! (%d>%d)\n", g_numprimindices, MAX_MAP_PRIMINDICES );
+ }
+ }
+ delete [] pStripIndices;
+#else
+ for( i = 0; i < triListIndices.Size(); i++ )
+ {
+ g_primindices[newPrim.firstIndex + newPrim.indexCount] = triListIndices[i];
+ newPrim.indexCount++;
+ g_numprimindices++;
+ if( g_numprimindices > MAX_MAP_PRIMINDICES )
+ {
+ Error( "Exceeded max water indicies.\nIncrease surface subdivision size! (%d>%d)\n", g_numprimindices, MAX_MAP_PRIMINDICES );
+ }
+ }
+#endif
+ g_numprimitives++; // don't increment until we get here and are sure that we have a primitive.
+ if( g_numprimitives > MAX_MAP_PRIMITIVES )
+ {
+ Error( "Exceeded max water primitives.\nIncrease surface subdivision size! (%d>%d)\n", ( int )g_numprimitives, ( int )MAX_MAP_PRIMITIVES );
+ }
+}
+
+void SubdivideFaceBySubdivSize( face_t *f )
+{
+ if( f->numpoints == 0 || f->split[0] || f->split[1] || f->merged || !f->w )
+ {
+ return;
+ }
+ // see if the face needs to be subdivided.
+ texinfo_t *pTexInfo = &texinfo[f->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ bool bFound;
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ MaterialSystemMaterial_t matID =
+ FindOriginalMaterial( pMaterialName, &bFound, false );
+
+ if( !bFound )
+ {
+ return;
+ }
+ const char *subdivsizeString = GetMaterialVar( matID, "$subdivsize" );
+ if( subdivsizeString )
+ {
+ float subdivSize = atof( subdivsizeString );
+ if( subdivSize > 0.0f )
+ {
+ // NOTE: Subdivision is unsupported and should be phased out
+ Warning("Using subdivision on %s\n", pMaterialName );
+ SubdivideFaceBySubdivSize( f, subdivSize );
+ }
+ }
+}
+
+void SplitSubdividedFaces_Node_r( node_t *node )
+{
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ return;
+ }
+ face_t *f;
+ for( f = node->faces; f ;f = f->next )
+ {
+ SubdivideFaceBySubdivSize( f );
+ }
+
+ //
+ // recursively output the other nodes
+ //
+ SplitSubdividedFaces_Node_r( node->children[0] );
+ SplitSubdividedFaces_Node_r( node->children[1] );
+}
+
+void SplitSubdividedFaces( face_t *pLeafFaceList, node_t *headnode )
+{
+ // deal with leaf faces.
+ face_t *f = pLeafFaceList;
+ while ( f )
+ {
+ SubdivideFaceBySubdivSize( f );
+ f = f->next;
+ }
+
+ // deal with node faces.
+ SplitSubdividedFaces_Node_r( headnode );
+}
+
+#pragma optimize( "", on )
+
+/*
+============
+MakeFaces
+============
+*/
+void MakeFaces (node_t *node)
+{
+ qprintf ("--- MakeFaces ---\n");
+ c_merge = 0;
+ c_subdivide = 0;
+ c_nodefaces = 0;
+
+ MakeFaces_r (node);
+
+ qprintf ("%5i makefaces\n", c_nodefaces);
+ qprintf ("%5i merged\n", c_merge);
+ qprintf ("%5i subdivided\n", c_subdivide);
+}
\ No newline at end of file diff --git a/mp/src/utils/vbsp/faces.h b/mp/src/utils/vbsp/faces.h new file mode 100644 index 00000000..cfebc24f --- /dev/null +++ b/mp/src/utils/vbsp/faces.h @@ -0,0 +1,20 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef FACES_H
+#define FACES_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void GetEdge2_InitOptimizedList(); // Call this before calling GetEdge2() on a bunch of edges.
+int AddEdge( int v1, int v2, face_t *f );
+int GetEdge2(int v1, int v2, face_t *f);
+
+
+#endif // FACES_H
diff --git a/mp/src/utils/vbsp/glfile.cpp b/mp/src/utils/vbsp/glfile.cpp new file mode 100644 index 00000000..236845bc --- /dev/null +++ b/mp/src/utils/vbsp/glfile.cpp @@ -0,0 +1,219 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+int c_glfaces;
+
+int PortalVisibleSides (portal_t *p)
+{
+ int fcon, bcon;
+
+ if (!p->onnode)
+ return 0; // outside
+
+ fcon = p->nodes[0]->contents;
+ bcon = p->nodes[1]->contents;
+
+ // same contents never create a face
+ if (fcon == bcon)
+ return 0;
+
+ // FIXME: is this correct now?
+ if (!fcon)
+ return 1;
+ if (!bcon)
+ return 2;
+ return 0;
+}
+
+void OutputWinding (winding_t *w, FileHandle_t glview)
+{
+ static int level = 128;
+ vec_t light;
+ int i;
+
+ CmdLib_FPrintf( glview, "%i\n", w->numpoints);
+ level+=28;
+ light = (level&255)/255.0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ light,
+ light,
+ light);
+ }
+ //CmdLib_FPrintf(glview, "\n");
+}
+
+void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b)
+{
+ int i;
+
+ CmdLib_FPrintf( glview, "%i\n", w->numpoints);
+ float lr = r * (1.0f/255.0f);
+ float lg = g * (1.0f/255.0f);
+ float lb = b * (1.0f/255.0f);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ CmdLib_FPrintf(glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ lr,
+ lg,
+ lb);
+ }
+ //CmdLib_FPrintf(glview, "\n");
+}
+
+/*
+=============
+OutputPortal
+=============
+*/
+void OutputPortal (portal_t *p, FileHandle_t glview)
+{
+ winding_t *w;
+ int sides;
+
+ sides = PortalVisibleSides (p);
+ if (!sides)
+ return;
+
+ c_glfaces++;
+
+ w = p->winding;
+
+ if (sides == 2) // back side
+ w = ReverseWinding (w);
+
+ OutputWinding (w, glview);
+
+ if (sides == 2)
+ FreeWinding(w);
+}
+
+/*
+=============
+WriteGLView_r
+=============
+*/
+void WriteGLView_r (node_t *node, FileHandle_t glview)
+{
+ portal_t *p, *nextp;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ WriteGLView_r (node->children[0], glview);
+ WriteGLView_r (node->children[1], glview);
+ return;
+ }
+
+ // write all the portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ if (p->nodes[0] == node)
+ {
+ OutputPortal (p, glview);
+ nextp = p->next[0];
+ }
+ else
+ nextp = p->next[1];
+ }
+}
+
+
+void WriteGLViewFaces_r( node_t *node, FileHandle_t glview )
+{
+ portal_t *p, *nextp;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ WriteGLViewFaces_r (node->children[0], glview);
+ WriteGLViewFaces_r (node->children[1], glview);
+ return;
+ }
+
+ // write all the portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ int s = (p->nodes[1] == node);
+
+ if ( p->face[s] )
+ {
+ OutputWinding( p->face[s]->w, glview );
+ }
+ nextp = p->next[s];
+ }
+}
+
+/*
+=============
+WriteGLView
+=============
+*/
+void WriteGLView (tree_t *tree, char *source)
+{
+ char name[1024];
+ FileHandle_t glview;
+
+ c_glfaces = 0;
+ sprintf (name, "%s%s.gl",outbase, source);
+ Msg("Writing %s\n", name);
+
+ glview = g_pFileSystem->Open( name, "w" );
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ WriteGLView_r (tree->headnode, glview);
+ g_pFileSystem->Close( glview );
+
+ Msg("%5i c_glfaces\n", c_glfaces);
+}
+
+
+void WriteGLViewFaces( tree_t *tree, const char *pName )
+{
+ char name[1024];
+ FileHandle_t glview;
+
+ c_glfaces = 0;
+ sprintf (name, "%s%s.gl", outbase, pName);
+ Msg("Writing %s\n", name);
+
+ glview = g_pFileSystem->Open( name, "w" );
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ WriteGLViewFaces_r (tree->headnode, glview);
+ g_pFileSystem->Close( glview );
+
+ Msg("%5i c_glfaces\n", c_glfaces);
+}
+
+
+void WriteGLViewBrushList( bspbrush_t *pList, const char *pName )
+{
+ char name[1024];
+ FileHandle_t glview;
+
+ sprintf (name, "%s%s.gl", outbase, pName );
+ Msg("Writing %s\n", name);
+
+ glview = g_pFileSystem->Open( name, "w" );
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ for ( bspbrush_t *pBrush = pList; pBrush; pBrush = pBrush->next )
+ {
+ for (int i = 0; i < pBrush->numsides; i++ )
+ OutputWinding( pBrush->sides[i].winding, glview );
+ }
+ g_pFileSystem->Close( glview );
+}
diff --git a/mp/src/utils/vbsp/ivp.cpp b/mp/src/utils/vbsp/ivp.cpp new file mode 100644 index 00000000..421b1b2e --- /dev/null +++ b/mp/src/utils/vbsp/ivp.cpp @@ -0,0 +1,1656 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdio.h>
+#include "mathlib/vector.h"
+#include "bspfile.h"
+#include "bsplib.h"
+#include "cmdlib.h"
+#include "physdll.h"
+#include "utlvector.h"
+#include "vbsp.h"
+#include "phyfile.h"
+#include <float.h>
+#include "KeyValues.h"
+#include "UtlBuffer.h"
+#include "utlsymbol.h"
+#include "utlrbtree.h"
+#include "ivp.h"
+#include "disp_ivp.h"
+#include "materialpatch.h"
+#include "bitvec.h"
+
+// bit per leaf
+typedef CBitVec<MAX_MAP_LEAFS> leafbitarray_t;
+
+// parameters for conversion to vphysics
+#define NO_SHRINK 0.0f
+// NOTE: vphysics maintains a minimum separation radius between objects
+// This radius is set to 0.25, but it's symmetric. So shrinking potentially moveable
+// brushes by 0.5 in every direction ensures that these brushes can be constructed
+// touching the world, and constrained in place without collisions or friction
+// UNDONE: Add a key to disable this shrinking if necessary
+#define VPHYSICS_SHRINK (0.5f) // shrink BSP brushes by this much for collision
+#define VPHYSICS_MERGE 0.01f // merge verts closer than this
+
+void EmitPhysCollision();
+
+IPhysicsCollision *physcollision = NULL;
+extern IPhysicsSurfaceProps *physprops;
+
+// a list of all of the materials in the world model
+static CUtlVector<int> s_WorldPropList;
+
+//-----------------------------------------------------------------------------
+// Purpose: Write key/value pairs out to a memory buffer
+//-----------------------------------------------------------------------------
+CTextBuffer::CTextBuffer( void )
+{
+}
+CTextBuffer::~CTextBuffer( void )
+{
+}
+
+void CTextBuffer::WriteText( const char *pText )
+{
+ int len = strlen( pText );
+ CopyData( pText, len );
+}
+
+void CTextBuffer::WriteIntKey( const char *pKeyName, int outputData )
+{
+ char tmp[1024];
+
+ // FAIL!
+ if ( strlen(pKeyName) > 1000 )
+ {
+ Msg("Error writing collision data %s\n", pKeyName );
+ return;
+ }
+ sprintf( tmp, "\"%s\" \"%d\"\n", pKeyName, outputData );
+ CopyData( tmp, strlen(tmp) );
+}
+
+void CTextBuffer::WriteStringKey( const char *pKeyName, const char *outputData )
+{
+ CopyStringQuotes( pKeyName );
+ CopyData( " ", 1 );
+ CopyStringQuotes( outputData );
+ CopyData( "\n", 1 );
+}
+
+void CTextBuffer::WriteFloatKey( const char *pKeyName, float outputData )
+{
+ char tmp[1024];
+
+ // FAIL!
+ if ( strlen(pKeyName) > 1000 )
+ {
+ Msg("Error writing collision data %s\n", pKeyName );
+ return;
+ }
+ sprintf( tmp, "\"%s\" \"%f\"\n", pKeyName, outputData );
+ CopyData( tmp, strlen(tmp) );
+}
+
+void CTextBuffer::WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count )
+{
+ char tmp[1024];
+
+ // FAIL!
+ if ( strlen(pKeyName) > 1000 )
+ {
+ Msg("Error writing collision data %s\n", pKeyName );
+ return;
+ }
+ sprintf( tmp, "\"%s\" \"", pKeyName );
+ for ( int i = 0; i < count; i++ )
+ {
+ char buf[80];
+
+ sprintf( buf, "%f ", outputData[i] );
+ strcat( tmp, buf );
+ }
+ strcat( tmp, "\"\n" );
+
+ CopyData( tmp, strlen(tmp) );
+}
+
+void CTextBuffer::CopyStringQuotes( const char *pString )
+{
+ CopyData( "\"", 1 );
+ CopyData( pString, strlen(pString) );
+ CopyData( "\"", 1 );
+}
+
+void CTextBuffer::Terminate( void )
+{
+ CopyData( "\0", 1 );
+}
+
+void CTextBuffer::CopyData( const char *pData, int len )
+{
+ int offset = m_buffer.AddMultipleToTail( len );
+ memcpy( m_buffer.Base() + offset, pData, len );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Writes a glview text file containing the collision surface in question
+// Input : *pCollide -
+// *pFilename -
+//-----------------------------------------------------------------------------
+void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename )
+{
+ if ( !pCollide )
+ return;
+
+ Msg("Writing %s...\n", pFilename );
+ Vector *outVerts;
+ int vertCount = physcollision->CreateDebugMesh( pCollide, &outVerts );
+ FILE *fp = fopen( pFilename, "w" );
+ int triCount = vertCount / 3;
+ int vert = 0;
+ for ( int i = 0; i < triCount; i++ )
+ {
+ fprintf( fp, "3\n" );
+ fprintf( fp, "%6.3f %6.3f %6.3f 1 0 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f 0 1 0\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f 0 0 1\n", outVerts[vert].x, outVerts[vert].y, outVerts[vert].z );
+ vert++;
+ }
+ fclose( fp );
+ physcollision->DestroyDebugMesh( vertCount, outVerts );
+}
+
+
+void DumpCollideToPHY( CPhysCollide *pCollide, CTextBuffer *text, const char *pFilename )
+{
+ Msg("Writing %s...\n", pFilename );
+ FILE *fp = fopen( pFilename, "wb" );
+ phyheader_t header;
+ header.size = sizeof(header);
+ header.id = 0;
+ header.checkSum = 0;
+ header.solidCount = 1;
+ fwrite( &header, sizeof(header), 1, fp );
+ int size = physcollision->CollideSize( pCollide );
+ fwrite( &size, sizeof(int), 1, fp );
+
+ char *buf = (char *)malloc( size );
+ physcollision->CollideWrite( buf, pCollide );
+ fwrite( buf, size, 1, fp );
+
+ fwrite( text->GetData(), text->GetSize(), 1, fp );
+ fclose( fp );
+ free( buf );
+}
+
+CPhysCollisionEntry::CPhysCollisionEntry( CPhysCollide *pCollide )
+{
+ m_pCollide = pCollide;
+}
+
+unsigned int CPhysCollisionEntry::GetCollisionBinarySize()
+{
+ return physcollision->CollideSize( m_pCollide );
+}
+
+unsigned int CPhysCollisionEntry::WriteCollisionBinary( char *pDest )
+{
+ return physcollision->CollideWrite( pDest, m_pCollide );
+}
+
+void CPhysCollisionEntry::DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer )
+{
+ char tmp[128];
+ sprintf( tmp, "%s%03d.phy", pName, modelIndex );
+ DumpCollideToPHY( m_pCollide, pTextBuffer, tmp );
+ sprintf( tmp, "%s%03d.txt", pName, modelIndex );
+ DumpCollideToGlView( m_pCollide, tmp );
+}
+
+
+class CPhysCollisionEntrySolid : public CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ float m_volume;
+ float m_mass;
+ const char *m_pMaterial;
+};
+
+
+CPhysCollisionEntrySolid::CPhysCollisionEntrySolid( CPhysCollide *pCollide, const char *pMaterialName, float mass )
+ : CPhysCollisionEntry( pCollide )
+{
+ m_volume = physcollision->CollideVolume( m_pCollide );
+ m_mass = mass;
+ m_pMaterial = pMaterialName;
+}
+
+void CPhysCollisionEntrySolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ DumpCollideFileName( "collide", modelIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntrySolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "solid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteFloatKey( "mass", m_mass );
+ if ( m_pMaterial )
+ {
+ pTextBuffer->WriteStringKey( "surfaceprop", m_pMaterial );
+ }
+ if ( m_volume != 0.f )
+ {
+ pTextBuffer->WriteFloatKey( "volume", m_volume );
+ }
+ pTextBuffer->WriteText( "}\n" );
+}
+
+
+class CPhysCollisionEntryStaticSolid : public CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ int m_contentsMask;
+};
+
+
+CPhysCollisionEntryStaticSolid ::CPhysCollisionEntryStaticSolid ( CPhysCollide *pCollide, int contentsMask )
+ : CPhysCollisionEntry( pCollide ), m_contentsMask(contentsMask)
+{
+}
+
+void CPhysCollisionEntryStaticSolid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ char tmp[128];
+ sprintf( tmp, "static%02d", modelIndex );
+ DumpCollideFileName( tmp, collideIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntryStaticSolid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "staticsolid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteIntKey( "contents", m_contentsMask );
+ pTextBuffer->WriteText( "}\n" );
+}
+
+CPhysCollisionEntryStaticMesh::CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName )
+ : CPhysCollisionEntry( pCollide )
+{
+ m_pMaterial = pMaterialName;
+}
+
+void CPhysCollisionEntryStaticMesh::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ char tmp[128];
+ sprintf( tmp, "mesh%02d", modelIndex );
+ DumpCollideFileName( tmp, collideIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntryStaticMesh::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "staticsolid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteText( "}\n" );
+}
+
+class CPhysCollisionEntryFluid : public CPhysCollisionEntry
+{
+public:
+ ~CPhysCollisionEntryFluid();
+ CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ char *m_pSurfaceProp;
+ float m_damping;
+ Vector m_surfaceNormal;
+ float m_surfaceDist;
+ int m_contentsMask;
+};
+
+
+CPhysCollisionEntryFluid::CPhysCollisionEntryFluid( CPhysCollide *pCollide, const char *pSurfaceProp, float damping, const Vector &normal, float dist, int nContents )
+ : CPhysCollisionEntry( pCollide )
+{
+ m_surfaceNormal = normal;
+ m_surfaceDist = dist;
+ m_pSurfaceProp = new char[strlen(pSurfaceProp)+1];
+ strcpy( m_pSurfaceProp, pSurfaceProp );
+ m_damping = damping;
+ m_contentsMask = nContents;
+}
+
+CPhysCollisionEntryFluid::~CPhysCollisionEntryFluid()
+{
+ delete[] m_pSurfaceProp;
+}
+
+void CPhysCollisionEntryFluid::DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ char tmp[128];
+ sprintf( tmp, "water%02d", modelIndex );
+ DumpCollideFileName( tmp, collideIndex, pTextBuffer );
+}
+
+void CPhysCollisionEntryFluid::WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex )
+{
+ pTextBuffer->WriteText( "fluid {\n" );
+ pTextBuffer->WriteIntKey( "index", collideIndex );
+ pTextBuffer->WriteStringKey( "surfaceprop", m_pSurfaceProp ); // write out water material
+ pTextBuffer->WriteFloatKey( "damping", m_damping ); // write out water damping
+ pTextBuffer->WriteIntKey( "contents", m_contentsMask ); // write out water contents
+ float array[4];
+ m_surfaceNormal.CopyToArray( array );
+ array[3] = m_surfaceDist;
+ pTextBuffer->WriteFloatArrayKey( "surfaceplane", array, 4 ); // write out water surface plane
+ pTextBuffer->WriteFloatArrayKey( "currentvelocity", vec3_origin.Base(), 3 ); // write out water velocity
+ pTextBuffer->WriteText( "}\n" );
+}
+
+// Get an index into the prop list of this prop (add it if necessary)
+static int PropIndex( CUtlVector<int> &propList, int propIndex )
+{
+ for ( int i = 0; i < propList.Count(); i++ )
+ {
+ if ( propList[i] == propIndex )
+ return i+1;
+ }
+
+ if ( propList.Count() < 126 )
+ {
+ return propList.AddToTail( propIndex )+1;
+ }
+
+ return 0;
+}
+
+int RemapWorldMaterial( int materialIndexIn )
+{
+ return PropIndex( s_WorldPropList, materialIndexIn );
+}
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+} listplane_t;
+
+static void AddListPlane( CUtlVector<listplane_t> *list, float x, float y, float z, float d )
+{
+ listplane_t plane;
+ plane.normal[0] = x;
+ plane.normal[1] = y;
+ plane.normal[2] = z;
+ plane.dist = d;
+
+ list->AddToTail( plane );
+}
+
+class CPlaneList
+{
+public:
+
+ CPlaneList( float shrink, float merge );
+ ~CPlaneList( void );
+
+ void AddConvex( CPhysConvex *pConvex );
+
+ // add the brushes to the model
+ int AddBrushes( void );
+
+ // Adds a single brush as a convex object
+ void ReferenceBrush( int brushnumber );
+ bool IsBrushReferenced( int brushnumber );
+
+ void ReferenceLeaf( int leafIndex );
+ bool IsLeafReferenced( int leafIndex );
+ int GetFirstBrushSide();
+
+private:
+
+ CPhysConvex *CPlaneList::BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum );
+
+public:
+ CUtlVector<CPhysConvex *> m_convex;
+
+ CUtlVector<int> m_leafList;
+ int m_contentsMask;
+
+ float m_shrink;
+ float m_merge;
+ bool *m_brushAdded;
+ float m_totalVolume;
+};
+
+CPlaneList::CPlaneList( float shrink, float merge )
+{
+ m_shrink = shrink;
+ m_merge = merge;
+ m_contentsMask = MASK_SOLID;
+ m_brushAdded = new bool[numbrushes];
+ memset( m_brushAdded, 0, sizeof(bool) * numbrushes );
+ m_totalVolume = 0;
+ m_leafList.Purge();
+}
+
+
+CPlaneList::~CPlaneList( void )
+{
+ delete[] m_brushAdded;
+}
+
+
+void CPlaneList::AddConvex( CPhysConvex *pConvex )
+{
+ if ( pConvex )
+ {
+ m_totalVolume += physcollision->ConvexVolume( pConvex );
+ m_convex.AddToTail( pConvex );
+ }
+}
+
+// Adds a single brush as a convex object
+void CPlaneList::ReferenceBrush( int brushnumber )
+{
+ if ( !(dbrushes[brushnumber].contents & m_contentsMask) )
+ return;
+
+ m_brushAdded[brushnumber] = true;
+
+}
+
+
+bool CPlaneList::IsBrushReferenced( int brushnumber )
+{
+ return m_brushAdded[brushnumber];
+}
+
+CPhysConvex *CPlaneList::BuildConvexForBrush( int brushnumber, float shrink, CPhysCollide *pCollideTest, float shrinkMinimum )
+{
+ CUtlVector<listplane_t> temp( 0, 32 );
+
+ for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
+ {
+ dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
+ if ( pside->bevel )
+ continue;
+
+ dplane_t *pplane = dplanes + pside->planenum;
+ float shrinkThisPlane = shrink;
+
+ if ( i < g_MainMap->mapbrushes[brushnumber].numsides )
+ {
+ if ( !g_MainMap->mapbrushes[brushnumber].original_sides[i].visible )
+ {
+ // don't shrink brush sides with no visible components.
+ // this produces something closer to the ideal shrink than simply shrinking all planes
+ shrinkThisPlane = 0;
+ }
+ }
+ // Make sure shrinking won't swallow geometry along this axis.
+ if ( pCollideTest && shrinkThisPlane != 0 )
+ {
+ Vector start = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, pplane->normal );
+ Vector end = physcollision->CollideGetExtent( pCollideTest, vec3_origin, vec3_angle, -pplane->normal );
+ float thick = DotProduct( (end-start), pplane->normal );
+ // NOTE: The object must be at least "shrinkMinimum" inches wide on each axis
+ if ( fabs(thick) < shrinkMinimum )
+ {
+#if _DEBUG
+ Warning("Can't shrink brush %d, plane %d (%.2f, %.2f, %.2f)\n", brushnumber, pside->planenum, pplane->normal[0], pplane->normal[1], pplane->normal[2] );
+#endif
+ shrinkThisPlane = 0;
+ }
+ }
+ AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist - shrinkThisPlane );
+ }
+ return physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), m_merge );
+}
+
+int CPlaneList::AddBrushes( void )
+{
+ int count = 0;
+ for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
+ {
+ if ( IsBrushReferenced(brushnumber) )
+ {
+ CPhysConvex *pBrushConvex = NULL;
+ if ( m_shrink != 0 )
+ {
+ // Make sure shrinking won't swallow this brush.
+ CPhysConvex *pConvex = BuildConvexForBrush( brushnumber, 0, NULL, 0 );
+ CPhysCollide *pUnshrunkCollide = physcollision->ConvertConvexToCollide( &pConvex, 1 );
+ pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, pUnshrunkCollide, m_shrink * 3 );
+ physcollision->DestroyCollide( pUnshrunkCollide );
+ }
+ else
+ {
+ pBrushConvex = BuildConvexForBrush( brushnumber, m_shrink, NULL, 1.0 );
+ }
+
+ if ( pBrushConvex )
+ {
+ count++;
+ physcollision->SetConvexGameData( pBrushConvex, brushnumber );
+ AddConvex( pBrushConvex );
+ }
+ }
+ }
+ return count;
+}
+
+
+int CPlaneList::GetFirstBrushSide()
+{
+ for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
+ {
+ if ( IsBrushReferenced(brushnumber) )
+ {
+ for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
+ {
+ int sideIndex = i + dbrushes[brushnumber].firstside;
+ dbrushside_t *pside = dbrushsides + sideIndex;
+ if ( pside->bevel )
+ continue;
+ return sideIndex;
+ }
+ }
+ }
+ return 0;
+}
+
+// UNDONE: Try using this kind of algorithm if we run into precision problems.
+// NOTE: ConvexFromPlanes will be doing a bunch of matrix inversions that can suffer
+// if plane normals are too close to each other...
+#if 0
+void CPlaneList::AddBrushes( void )
+{
+ CUtlVector<listplane_t> temp;
+ for ( int brushnumber = 0; brushnumber < numbrushes; brushnumber++ )
+ {
+ if ( IsBrushReferenced(brushnumber) )
+ {
+ CUtlVector<winding_t *> windings;
+
+ for ( int i = 0; i < dbrushes[brushnumber].numsides; i++ )
+ {
+ dbrushside_t *pside = dbrushsides + i + dbrushes[brushnumber].firstside;
+ if (pside->bevel)
+ continue;
+ dplane_t *pplane = dplanes + pside->planenum;
+ winding_t *w = BaseWindingForPlane( pplane->normal, pplane->dist - m_shrink );
+ for ( int j = 0; j < dbrushes[brushnumber].numsides && w; j++ )
+ {
+ if (i == j)
+ continue;
+ dbrushside_t *pClipSide = dbrushsides + j + dbrushes[brushnumber].firstside;
+ if (pClipSide->bevel)
+ continue;
+ dplane_t *pClipPlane = dplanes + pClipSide->planenum;
+ ChopWindingInPlace (&w, -pClipPlane->normal, -pClipPlane->dist+m_shrink, 0); //CLIP_EPSILON);
+ }
+ if ( w )
+ {
+ windings.AddToTail( w );
+ }
+ }
+
+ CUtlVector<Vector *> vertList;
+ for ( int p = 0; p < windings.Count(); p++ )
+ {
+ for ( int v = 0; v < windings[p]->numpoints; v++ )
+ {
+ vertList.AddToTail( windings[p]->p + v );
+ }
+ }
+ CPhysConvex *pConvex = physcollision->ConvexFromVerts( vertList.Base(), vertList.Count() );
+ if ( pConvex )
+ {
+ physcollision->SetConvexGameData( pConvex, brushnumber );
+ AddConvex( pConvex );
+ }
+ temp.RemoveAll();
+ }
+ }
+}
+#endif
+
+// If I have a list of leaves, make sure this leaf is in it.
+// Otherwise, process all leaves
+bool CPlaneList::IsLeafReferenced( int leafIndex )
+{
+ if ( !m_leafList.Count() )
+ return true;
+
+ for ( int i = 0; i < m_leafList.Count(); i++ )
+ {
+ if ( m_leafList[i] == leafIndex )
+ return true;
+ }
+
+ return false;
+}
+
+// Add a leaf to my list of interesting leaves
+void CPlaneList::ReferenceLeaf( int leafIndex )
+{
+ m_leafList.AddToTail( leafIndex );
+}
+
+static void VisitLeaves_r( CPlaneList &planes, int node )
+{
+ if ( node < 0 )
+ {
+ int leafIndex = -1 - node;
+ if ( planes.IsLeafReferenced(leafIndex) )
+ {
+ int i;
+
+ // Add the solids in the "empty" leaf
+ for ( i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
+ {
+ int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
+ planes.ReferenceBrush( brushIndex );
+ }
+ }
+ }
+ else
+ {
+ dnode_t *pnode = dnodes + node;
+
+ VisitLeaves_r( planes, pnode->children[0] );
+ VisitLeaves_r( planes, pnode->children[1] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+struct waterleaf_t
+{
+ Vector surfaceNormal;
+ float surfaceDist;
+ float minZ;
+ bool hasSurface;
+ int waterLeafIndex;// this is the submerged leaf
+ int planenum; //UNDONE: REMOVE
+ int surfaceTexInfo; // if hasSurface == true, this is the texinfo index for the water material
+ int outsideLeafIndex;// this is the leaf on the other side of the water surface
+ node_t *pNode;
+};
+
+
+
+// returns true if newleaf should appear before currentleaf in the list
+static bool IsLowerLeaf( const waterleaf_t &newleaf, const waterleaf_t ¤tleaf )
+{
+ if ( newleaf.hasSurface && currentleaf.hasSurface )
+ {
+ // the one with the upmost pointing z goes first
+ if ( currentleaf.surfaceNormal.z > newleaf.surfaceNormal.z )
+ return false;
+
+ if ( fabs(currentleaf.surfaceNormal.z - newleaf.surfaceNormal.z) < 0.01 )
+ {
+ if ( newleaf.surfaceDist < currentleaf.surfaceDist )
+ return true;
+ }
+ return true;
+ }
+ else if ( newleaf.hasSurface ) // the leaf with a surface always goes first
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Water surfaces are stored in an RB tree and the tree is used to
+// create one-off .vmt files embedded in the .bsp for each surface so that the
+// water depth effect occurs on a per-water surface level.
+//-----------------------------------------------------------------------------
+struct WaterTexInfo
+{
+ // The mangled new .vmt name ( materials/levelename/oldmaterial_depth_xxx ) where xxx is
+ // the water depth (as an integer )
+ CUtlSymbol m_FullName;
+
+ // The original .vmt name
+ CUtlSymbol m_MaterialName;
+
+ // The depth of the water this texinfo refers to
+ int m_nWaterDepth;
+
+ // The texinfo id
+ int m_nTexInfo;
+
+ // The subdivision size for the water surface
+// float m_SubdivSize;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper for RB tree operations ( we compare full mangled names )
+// Input : src1 -
+// src2 -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool WaterLessFunc( WaterTexInfo const& src1, WaterTexInfo const& src2 )
+{
+ return src1.m_FullName < src2.m_FullName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: A growable RB tree of water surfaces
+//-----------------------------------------------------------------------------
+static CUtlRBTree< WaterTexInfo, int > g_WaterTexInfos( 0, 32, WaterLessFunc );
+
+#if 0
+float GetSubdivSizeForFogVolume( int fogVolumeID )
+{
+ Assert( fogVolumeID >= 0 && fogVolumeID < g_WaterTexInfos.Count() );
+ return g_WaterTexInfos[fogVolumeID].m_SubdivSize;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *mapname -
+// *materialname -
+// waterdepth -
+// *fullname -
+//-----------------------------------------------------------------------------
+void GetWaterTextureName( char const *mapname, char const *materialname, int waterdepth, char *fullname )
+{
+ char temp[ 512 ];
+
+ // Construct the full name (prepend mapname to reduce name collisions)
+ sprintf( temp, "maps/%s/%s_depth_%i", mapname, materialname, (int)waterdepth );
+
+ // Make sure it's lower case
+ strlwr( temp );
+
+ strcpy( fullname, temp );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called to write procedural materials in the rb tree to the embedded
+// pak file for this .bsp
+//-----------------------------------------------------------------------------
+void EmitWaterMaterialFile( WaterTexInfo *wti )
+{
+ char waterTextureName[512];
+ if ( !wti )
+ {
+ return;
+ }
+
+ GetWaterTextureName( mapbase, wti->m_MaterialName.String(), ( int )wti->m_nWaterDepth, waterTextureName );
+
+ // Convert to string
+ char szDepth[ 32 ];
+ sprintf( szDepth, "%i", wti->m_nWaterDepth );
+ CreateMaterialPatch( wti->m_MaterialName.String(), waterTextureName, "$waterdepth", szDepth, PATCH_INSERT );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes the texinfo_t referenced by the .vmt and the computed depth for the
+// surface and looks up or creates a texdata/texinfo for the mangled one-off water .vmt file
+// Input : *pBaseInfo -
+// depth -
+// Output : int
+//-----------------------------------------------------------------------------
+int FindOrCreateWaterTexInfo( texinfo_t *pBaseInfo, float depth )
+{
+ char fullname[ 512 ];
+ char materialname[ 512 ];
+
+ // Get the base texture/material name
+ char const *name = TexDataStringTable_GetString( GetTexData( pBaseInfo->texdata )->nameStringTableID );
+
+ GetWaterTextureName( mapbase, name, (int)depth, fullname );
+
+ // See if we already have an entry for this depth
+ WaterTexInfo lookup;
+ lookup.m_FullName = fullname;
+ int idx = g_WaterTexInfos.Find( lookup );
+
+ // If so, return the existing entry texinfo index
+ if ( idx != g_WaterTexInfos.InvalidIndex() )
+ {
+ return g_WaterTexInfos[ idx ].m_nTexInfo;
+ }
+
+ // Otherwise, fill in the rest of the data
+ lookup.m_nWaterDepth = (int)depth;
+ // Remember the current material name
+ sprintf( materialname, "%s", name );
+ strlwr( materialname );
+ lookup.m_MaterialName = materialname;
+
+ texinfo_t ti;
+ // Make a copy
+ ti = *pBaseInfo;
+ // Create a texdata that is based on the underlying existing entry
+ ti.texdata = FindAliasedTexData( fullname, GetTexData( pBaseInfo->texdata ) );
+
+ // Find or create a new index
+ lookup.m_nTexInfo = FindOrCreateTexInfo( ti );
+
+ // Add the new texinfo to the RB tree
+ idx = g_WaterTexInfos.Insert( lookup );
+
+ // Msg( "created texinfo for %s\n", lookup.m_FullName.String() );
+
+ // Go ahead and create the new vmt file.
+ EmitWaterMaterialFile( &g_WaterTexInfos[idx] );
+
+ // Return the new texinfo
+ return g_WaterTexInfos[ idx ].m_nTexInfo;
+}
+
+extern node_t *dfacenodes[MAX_MAP_FACES];
+static void WriteFogVolumeIDs( dmodel_t *pModel )
+{
+ int i;
+
+ // write fog volume ID to each face in this model
+ for( i = pModel->firstface; i < pModel->firstface + pModel->numfaces; i++ )
+ {
+ dface_t *pFace = &dfaces[i];
+ node_t *pFaceNode = dfacenodes[i];
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ pFace->surfaceFogVolumeID = -1;
+ if ( pFaceNode )
+ {
+ if ( (pTexInfo->flags & SURF_WARP ) && pFaceNode->planenum == PLANENUM_LEAF && pFaceNode->diskId >= 0 )
+ {
+ pFace->surfaceFogVolumeID = dleafs[pFaceNode->diskId].leafWaterDataID;
+ dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[pFace->surfaceFogVolumeID];
+
+ // HACKHACK: Should probably mark these faces as water bottom or "bottommaterial" faces.
+ // HACKHACK: Use a heuristic, if it points up, it's the water top.
+ if ( dplanes[pFace->planenum].normal.z > 0 )
+ {
+ pFace->texinfo = pLeafWaterData->surfaceTexInfoID;
+ }
+ }
+ else
+ {
+ // missed this face somehow?
+ Assert( !(pTexInfo->flags & SURF_WARP ) );
+ }
+
+ }
+ }
+}
+
+
+static bool PortalCrossesWater( waterleaf_t &baseleaf, portal_t *portal )
+{
+ if ( baseleaf.hasSurface )
+ {
+ int side = WindingOnPlaneSide( portal->winding, baseleaf.surfaceNormal, baseleaf.surfaceDist );
+ if ( side == SIDE_CROSS || side == SIDE_FRONT )
+ return true;
+ }
+
+ return false;
+}
+
+
+static int FindOrCreateLeafWaterData( float surfaceZ, float minZ, int surfaceTexInfoID )
+{
+ int i;
+ for( i = 0; i < numleafwaterdata; i++ )
+ {
+ dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[i];
+ if( pLeafWaterData->surfaceZ == surfaceZ &&
+ pLeafWaterData->minZ == minZ &&
+ pLeafWaterData->surfaceTexInfoID == surfaceTexInfoID )
+ {
+ return i;
+ }
+ }
+ dleafwaterdata_t *pLeafWaterData = &dleafwaterdata[numleafwaterdata];
+ pLeafWaterData->surfaceZ = surfaceZ;
+ pLeafWaterData->minZ = minZ;
+ pLeafWaterData->surfaceTexInfoID = surfaceTexInfoID;
+ numleafwaterdata++;
+ return numleafwaterdata - 1;
+}
+
+
+// Enumerate all leaves under node with contents in contentsMask and add them to list
+void EnumLeaves_r( CUtlVector<node_t *> &list, node_t *node, int contentsMask )
+{
+ if ( node->planenum != PLANENUM_LEAF )
+ {
+ EnumLeaves_r( list, node->children[0], contentsMask );
+ EnumLeaves_r( list, node->children[1], contentsMask );
+ return;
+ }
+
+ if ( !(node->contents & contentsMask) )
+ return;
+
+
+ // has the contents, put it in the list
+ list.AddToTail( node );
+}
+
+
+// Builds a waterleaf_t for the given leaf
+static void BuildWaterLeaf( node_t *pLeafIn, waterleaf_t &waterLeafOut )
+{
+ waterLeafOut.pNode = pLeafIn;
+ waterLeafOut.waterLeafIndex = pLeafIn->diskId;
+ waterLeafOut.outsideLeafIndex = -1;
+ waterLeafOut.hasSurface = false;
+ waterLeafOut.surfaceDist = MAX_COORD_INTEGER;
+ waterLeafOut.surfaceNormal.Init( 0.f, 0.f, 1.f );
+ waterLeafOut.planenum = -1;
+ waterLeafOut.surfaceTexInfo = -1;
+ waterLeafOut.minZ = MAX_COORD_INTEGER;
+
+ // search the list of portals out of this leaf for one that leaves water
+ // If you find one, this leaf has a surface, so fill out the surface data
+ int oppositeNodeIndex = 0;
+ for (portal_t *p = pLeafIn->portals ; p ; p = p->next[!oppositeNodeIndex])
+ {
+ oppositeNodeIndex = (p->nodes[0] == pLeafIn) ? 1 : 0;
+
+ // not visible, can't be the portals we're looking for...
+ if ( !p->side )
+ continue;
+
+ // See if this portal crosses into air
+ node_t *pOpposite = p->nodes[oppositeNodeIndex];
+ if ( !(pOpposite->contents & MASK_WATER) && !(pOpposite->contents & MASK_SOLID) )
+ {
+ // it does, there must be a surface here
+ plane_t *plane = &g_MainMap->mapplanes[p->side->planenum];
+ if ( waterLeafOut.hasSurface )
+ {
+ // Sort to find the most upward facing normal (skips sides)
+ if ( waterLeafOut.surfaceNormal.z > plane->normal.z )
+ continue;
+ if ( (waterLeafOut.surfaceNormal.z == plane->normal.z) && waterLeafOut.surfaceDist >= plane->dist )
+ continue;
+ }
+ // water surface needs to point at least somewhat up, this is
+ // probably a map error
+ if ( plane->normal.z <= 0 )
+ continue;
+ waterLeafOut.surfaceDist = plane->dist;
+ waterLeafOut.surfaceNormal = plane->normal;
+ waterLeafOut.hasSurface = true;
+ waterLeafOut.outsideLeafIndex = p->nodes[oppositeNodeIndex]->diskId;
+ waterLeafOut.surfaceTexInfo = p->side->texinfo;
+ }
+ }
+}
+
+
+static void InsertSortWaterLeaf( CUtlVector<waterleaf_t> &list, const waterleaf_t &leafInsert )
+{
+ // insertion sort the leaf (lowest leaves go first)
+ // leaves that aren't actually on the surface of the water will have leaf.hasSurface == false.
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ if ( IsLowerLeaf( leafInsert, list[i] ) )
+ {
+ list.InsertBefore( i, leafInsert );
+ return;
+ }
+ }
+
+ // must the highest one, so stick it at the end.
+ list.AddToTail( leafInsert );
+}
+
+
+// Flood fill the tree, finding neighboring water volumes and connecting them to this list
+// Cut groups that try to cross the surface.
+// Mark leaves that are in a group as "visited" so they won't be chosen by subsequent fills
+static void Flood_FindConnectedWaterVolumes_r( CUtlVector<node_t *> &list, node_t *pLeaf, waterleaf_t &baseleaf, leafbitarray_t &visited )
+{
+ // already visited, or not the same water contents
+ if ( pLeaf->diskId < 0 || visited.Get(pLeaf->diskId) || !(pLeaf->contents & (baseleaf.pNode->contents & MASK_WATER) ) )
+ return;
+
+ int oppositeNodeIndex = 0;
+ for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
+ {
+ oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
+
+ // If any portal crosses the water surface, don't flow through this leaf
+ if ( PortalCrossesWater( baseleaf, p ) )
+ return;
+ }
+
+ visited.Set( pLeaf->diskId );
+ list.AddToTail( pLeaf );
+
+ baseleaf.minZ = min( pLeaf->mins.z, baseleaf.minZ );
+
+ for (portal_t *p = pLeaf->portals ; p ; p = p->next[!oppositeNodeIndex])
+ {
+ oppositeNodeIndex = (p->nodes[0] == pLeaf) ? 1 : 0;
+
+ Flood_FindConnectedWaterVolumes_r( list, p->nodes[oppositeNodeIndex], baseleaf, visited );
+ }
+}
+
+// UNDONE: This is a bit of a hack to avoid crashing when we can't find an
+// appropriate texinfo for a water model (to get physics properties)
+int FirstWaterTexinfo( bspbrush_t *brushlist, int contents )
+{
+ while (brushlist)
+ {
+ if ( brushlist->original->contents & contents )
+ {
+ for ( int i = 0; i < brushlist->original->numsides; i++ )
+ {
+ if ( brushlist->original->original_sides[i].contents & contents )
+ {
+ return brushlist->original->original_sides[i].texinfo;
+ }
+ }
+ }
+ brushlist = brushlist->next;
+ }
+
+ Assert(0);
+ return 0;
+}
+
+// This is a list of water data that will be turned into physics models
+struct watermodel_t
+{
+ int modelIndex;
+ int contents;
+ waterleaf_t waterLeafData;
+ int depthTexinfo;
+ int firstWaterLeafIndex;
+ int waterLeafCount;
+ int fogVolumeIndex;
+};
+
+static CUtlVector<watermodel_t> g_WaterModels;
+static CUtlVector<int> g_WaterLeafList;
+
+// Creates a list of watermodel_t for later processing by EmitPhysCollision
+void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *node )
+{
+ CUtlVector<node_t *> leafListAnyWater;
+ // build the list of all leaves containing water
+ EnumLeaves_r( leafListAnyWater, node, MASK_WATER );
+
+ // make a sorted list to flood fill
+ CUtlVector<waterleaf_t> list;
+
+ int i;
+ for ( i = 0; i < leafListAnyWater.Count(); i++ )
+ {
+ waterleaf_t waterLeaf;
+ BuildWaterLeaf( leafListAnyWater[i], waterLeaf );
+ InsertSortWaterLeaf( list, waterLeaf );
+ }
+
+ leafbitarray_t visited;
+ CUtlVector<node_t *> waterAreaList;
+ for ( i = 0; i < list.Count(); i++ )
+ {
+ Flood_FindConnectedWaterVolumes_r( waterAreaList, list[i].pNode, list[i], visited );
+
+ // did we find a list of leaves connected to this one?
+ // remember the list is sorted, so this one may have been attached to a previous
+ // leaf. So it could have nothing hanging off of it.
+ if ( waterAreaList.Count() )
+ {
+ // yes, emit a watermodel
+ watermodel_t tmp;
+ tmp.modelIndex = nummodels;
+ tmp.contents = list[i].pNode->contents;
+ tmp.waterLeafData = list[i];
+ tmp.firstWaterLeafIndex = g_WaterLeafList.Count();
+ tmp.waterLeafCount = waterAreaList.Count();
+
+ float waterDepth = tmp.waterLeafData.surfaceDist - tmp.waterLeafData.minZ;
+ if ( tmp.waterLeafData.surfaceTexInfo < 0 )
+ {
+ // the map has probably leaked in this case, but output something anyway.
+ Assert(list[i].pNode->planenum == PLANENUM_LEAF);
+ tmp.waterLeafData.surfaceTexInfo = FirstWaterTexinfo( list[i].pNode->brushlist, tmp.contents );
+ }
+ tmp.depthTexinfo = FindOrCreateWaterTexInfo( &texinfo[ tmp.waterLeafData.surfaceTexInfo ], waterDepth );
+ tmp.fogVolumeIndex = FindOrCreateLeafWaterData( tmp.waterLeafData.surfaceDist, tmp.waterLeafData.minZ, tmp.waterLeafData.surfaceTexInfo );
+
+ for ( int j = 0; j < waterAreaList.Count(); j++ )
+ {
+ g_WaterLeafList.AddToTail( waterAreaList[j]->diskId );
+ }
+ waterAreaList.RemoveAll();
+ g_WaterModels.AddToTail( tmp );
+ }
+ }
+
+ WriteFogVolumeIDs( pModel );
+}
+
+
+static void ConvertWaterModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex,
+ float shrinkSize, float mergeTolerance )
+{
+ dmodel_t *pModel = dmodels + modelIndex;
+
+ for ( int i = 0; i < g_WaterModels.Count(); i++ )
+ {
+ watermodel_t &waterModel = g_WaterModels[i];
+ if ( waterModel.modelIndex != modelIndex )
+ continue;
+
+ CPlaneList planes( shrinkSize, mergeTolerance );
+ int firstLeaf = waterModel.firstWaterLeafIndex;
+ planes.m_contentsMask = waterModel.contents;
+
+ // push all of the leaves into the collision list
+ for ( int j = 0; j < waterModel.waterLeafCount; j++ )
+ {
+ int leafIndex = g_WaterLeafList[firstLeaf + j];
+
+ dleaf_t *pLeaf = dleafs + leafIndex;
+ // fixup waterdata
+ pLeaf->leafWaterDataID = waterModel.fogVolumeIndex;
+ planes.ReferenceLeaf( leafIndex );
+ }
+
+ // visit the referenced leaves that belong to this model
+ VisitLeaves_r( planes, pModel->headnode );
+
+ // Now add the brushes from those leaves as convex
+
+ // BUGBUG: NOTE: If your map has a brush that crosses the surface, it will be added to two water
+ // volumes. This only happens with connected water volumes with multiple surface heights
+ // UNDONE: Right now map makers must cut such brushes. It could be automatically cut by adding the
+ // surface plane to the list for each brush before calling ConvexFromPlanes()
+ planes.AddBrushes();
+
+ int count = planes.m_convex.Count();
+ if ( !count )
+ continue;
+
+ // Save off the plane of the surface for this group as well as the collision model
+ // for all convex objects in the group.
+ CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
+ if ( pCollide )
+ {
+ int waterSurfaceTexInfoID = -1;
+ // use defaults
+ const char *pSurfaceProp = "water";
+ float damping = 0.01;
+ if ( waterSurfaceTexInfoID >= 0 )
+ {
+ // material override
+ int texdata = texinfo[waterSurfaceTexInfoID].texdata;
+ int prop = g_SurfaceProperties[texdata];
+ pSurfaceProp = physprops->GetPropName( prop );
+ }
+
+ if ( !waterModel.waterLeafData.hasSurface )
+ {
+ waterModel.waterLeafData.surfaceNormal.Init( 0,0,1 );
+ Vector top = physcollision->CollideGetExtent( pCollide, vec3_origin, vec3_angle, waterModel.waterLeafData.surfaceNormal );
+ waterModel.waterLeafData.surfaceDist = top.z;
+ }
+ CPhysCollisionEntryFluid *pCollisionEntryFuild = new CPhysCollisionEntryFluid( pCollide,
+ pSurfaceProp, damping, waterModel.waterLeafData.surfaceNormal, waterModel.waterLeafData.surfaceDist, waterModel.contents );
+ collisionList.AddToTail( pCollisionEntryFuild );
+ }
+ }
+}
+
+// compute a normal for a triangle of the given three points (points are clockwise, normal points out)
+static Vector TriangleNormal( const Vector &p0, const Vector &p1, const Vector &p2 )
+{
+ Vector e0 = p1 - p0;
+ Vector e1 = p2 - p0;
+ Vector normal = CrossProduct( e1, e0 );
+ VectorNormalize( normal );
+
+ return normal;
+}
+
+
+// find the side of the brush with the normal closest to the given normal
+static dbrushside_t *FindBrushSide( int brushIndex, const Vector &normal )
+{
+ dbrush_t *pbrush = &dbrushes[brushIndex];
+ dbrushside_t *out = NULL;
+ float best = -1.f;
+
+ for ( int i = 0; i < pbrush->numsides; i++ )
+ {
+ dbrushside_t *pside = dbrushsides + i + pbrush->firstside;
+ dplane_t *pplane = dplanes + pside->planenum;
+ float dot = DotProduct( normal, pplane->normal );
+ if ( dot > best )
+ {
+ best = dot;
+ out = pside;
+ }
+ }
+
+ return out;
+}
+
+
+
+static void ConvertWorldBrushesToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance, int contentsMask )
+{
+ CPlaneList planes( shrinkSize, mergeTolerance );
+
+ planes.m_contentsMask = contentsMask;
+
+ VisitLeaves_r( planes, dmodels[0].headnode );
+ planes.AddBrushes();
+
+ int count = planes.m_convex.Count();
+ if ( count )
+ {
+ CPhysCollide *pCollide = physcollision->ConvertConvexToCollide( planes.m_convex.Base(), count );
+
+ ICollisionQuery *pQuery = physcollision->CreateQueryModel( pCollide );
+ int convex = pQuery->ConvexCount();
+ for ( int i = 0; i < convex; i++ )
+ {
+ int triCount = pQuery->TriangleCount( i );
+ int brushIndex = pQuery->GetGameData( i );
+
+ Vector points[3];
+ for ( int j = 0; j < triCount; j++ )
+ {
+ pQuery->GetTriangleVerts( i, j, points );
+ Vector normal = TriangleNormal( points[0], points[1], points[2] );
+ dbrushside_t *pside = FindBrushSide( brushIndex, normal );
+ if ( pside->texinfo != TEXINFO_NODE )
+ {
+ int prop = g_SurfaceProperties[texinfo[pside->texinfo].texdata];
+ pQuery->SetTriangleMaterialIndex( i, j, RemapWorldMaterial( prop ) );
+ }
+ }
+ }
+ physcollision->DestroyQueryModel( pQuery );
+ pQuery = NULL;
+
+ collisionList.AddToTail( new CPhysCollisionEntryStaticSolid( pCollide, contentsMask ) );
+ }
+}
+
+// adds any world, terrain, and water collision models to the collision list
+static void BuildWorldPhysModel( CUtlVector<CPhysCollisionEntry *> &collisionList, float shrinkSize, float mergeTolerance )
+{
+ ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, MASK_SOLID );
+ ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_PLAYERCLIP );
+ ConvertWorldBrushesToPhysCollide( collisionList, shrinkSize, mergeTolerance, CONTENTS_MONSTERCLIP );
+
+ if ( !g_bNoVirtualMesh && Disp_HasPower4Displacements() )
+ {
+ Warning("WARNING: Map using power 4 displacements, terrain physics cannot be compressed, map will need additional memory and CPU.\n");
+ g_bNoVirtualMesh = true;
+ }
+
+ // if there's terrain, save it off as a static mesh/polysoup
+ if ( g_bNoVirtualMesh || !physcollision->SupportsVirtualMesh() )
+ {
+ Disp_AddCollisionModels( collisionList, &dmodels[0], MASK_SOLID );
+ }
+ else
+ {
+ Disp_BuildVirtualMesh( MASK_SOLID );
+ }
+ ConvertWaterModelToPhysCollide( collisionList, 0, shrinkSize, mergeTolerance );
+}
+
+
+// adds a collision entry for this brush model
+static void ConvertModelToPhysCollide( CUtlVector<CPhysCollisionEntry *> &collisionList, int modelIndex, int contents, float shrinkSize, float mergeTolerance )
+{
+ int i;
+ CPlaneList planes( shrinkSize, mergeTolerance );
+
+ planes.m_contentsMask = contents;
+
+ dmodel_t *pModel = dmodels + modelIndex;
+ VisitLeaves_r( planes, pModel->headnode );
+ planes.AddBrushes();
+ int count = planes.m_convex.Count();
+ convertconvexparams_t params;
+ params.Defaults();
+ params.buildOuterConvexHull = count > 1 ? true : false;
+ params.buildDragAxisAreas = true;
+ Vector size = pModel->maxs - pModel->mins;
+
+ float minSurfaceArea = -1.0f;
+ for ( i = 0; i < 3; i++ )
+ {
+ int other = (i+1)%3;
+ int cross = (i+2)%3;
+ float surfaceArea = size[other] * size[cross];
+ if ( minSurfaceArea < 0 || surfaceArea < minSurfaceArea )
+ {
+ minSurfaceArea = surfaceArea;
+ }
+ }
+ // this can be really slow with super-large models and a low error tolerance
+ // Basically you get a ray cast through each square of epsilon surface area on each OBB side
+ // So compute it for 1% error (on the smallest side, less on larger sides)
+ params.dragAreaEpsilon = clamp( minSurfaceArea * 1e-2f, 1.0f, 1024.0f );
+ CPhysCollide *pCollide = physcollision->ConvertConvexToCollideParams( planes.m_convex.Base(), count, params );
+
+ if ( !pCollide )
+ return;
+
+ struct
+ {
+ int prop;
+ float area;
+ } proplist[256];
+ int numprops = 1;
+
+ proplist[0].prop = -1;
+ proplist[0].area = 1;
+ // compute the array of props on the surface of this model
+
+ // NODRAW brushes no longer have any faces
+ if ( !dmodels[modelIndex].numfaces )
+ {
+ int sideIndex = planes.GetFirstBrushSide();
+ int texdata = texinfo[dbrushsides[sideIndex].texinfo].texdata;
+ int prop = g_SurfaceProperties[texdata];
+ proplist[numprops].prop = prop;
+ proplist[numprops].area = 2;
+ numprops++;
+ }
+
+ for ( i = 0; i < dmodels[modelIndex].numfaces; i++ )
+ {
+ dface_t *face = dfaces + i + dmodels[modelIndex].firstface;
+ int texdata = texinfo[face->texinfo].texdata;
+ int prop = g_SurfaceProperties[texdata];
+ int j;
+ for ( j = 0; j < numprops; j++ )
+ {
+ if ( proplist[j].prop == prop )
+ {
+ proplist[j].area += face->area;
+ break;
+ }
+ }
+
+ if ( (!numprops || j >= numprops) && numprops < ARRAYSIZE(proplist) )
+ {
+ proplist[numprops].prop = prop;
+ proplist[numprops].area = face->area;
+ numprops++;
+ }
+ }
+
+
+ // choose the prop with the most surface area
+ int maxIndex = -1;
+ float maxArea = 0;
+ float totalArea = 0;
+
+ for ( i = 0; i < numprops; i++ )
+ {
+ if ( proplist[i].area > maxArea )
+ {
+ maxIndex = i;
+ maxArea = proplist[i].area;
+ }
+ // add up the total surface area
+ totalArea += proplist[i].area;
+ }
+
+ float mass = 1.0f;
+ const char *pMaterial = "default";
+ if ( maxIndex >= 0 )
+ {
+ int prop = proplist[maxIndex].prop;
+
+ // use default if this material has no prop
+ if ( prop < 0 )
+ prop = 0;
+
+ pMaterial = physprops->GetPropName( prop );
+ float density, thickness;
+ physprops->GetPhysicsProperties( prop, &density, &thickness, NULL, NULL );
+
+ // if this is a "shell" material (it is hollow and encloses some empty space)
+ // compute the mass with a constant surface thickness
+ if ( thickness != 0 )
+ {
+ mass = totalArea * thickness * density * CUBIC_METERS_PER_CUBIC_INCH;
+ }
+ else
+ {
+ // material is completely solid, compute total mass as if constant density throughout.
+ mass = planes.m_totalVolume * density * CUBIC_METERS_PER_CUBIC_INCH;
+ }
+ }
+
+ // Clamp mass to 100,000 kg
+ if ( mass > VPHYSICS_MAX_MASS )
+ {
+ mass = VPHYSICS_MAX_MASS;
+ }
+
+ collisionList.AddToTail( new CPhysCollisionEntrySolid( pCollide, pMaterial, mass ) );
+}
+
+static void ClearLeafWaterData( void )
+{
+ int i;
+
+ for( i = 0; i < numleafs; i++ )
+ {
+ dleafs[i].leafWaterDataID = -1;
+ dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
+ }
+}
+
+
+// This is the only public entry to this file.
+// The global data touched in the file is:
+// from bsplib.h:
+// g_pPhysCollide : This is an output from this file.
+// g_PhysCollideSize : This is set in this file.
+// g_numdispinfo : This is an input to this file.
+// g_dispinfo : This is an input to this file.
+// numnodewaterdata : This is an output from this file.
+// dleafwaterdata : This is an output from this file.
+// from vbsp.h:
+// g_SurfaceProperties : This is an input to this file.
+void EmitPhysCollision()
+{
+ ClearLeafWaterData();
+
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( physicsFactory )
+ {
+ physcollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ }
+
+ if ( !physcollision )
+ {
+ Warning("!!! WARNING: Can't build collision data!\n" );
+ return;
+ }
+
+ CUtlVector<CPhysCollisionEntry *> collisionList[MAX_MAP_MODELS];
+ CTextBuffer *pTextBuffer[MAX_MAP_MODELS];
+
+ int physModelCount = 0, totalSize = 0;
+
+ int start = Plat_FloatTime();
+
+ Msg("Building Physics collision data...\n" );
+
+ int i, j;
+ for ( i = 0; i < nummodels; i++ )
+ {
+ // Build a list of collision models for this brush model section
+ if ( i == 0 )
+ {
+ // world is the only model that processes water separately.
+ // other brushes are assumed to be completely solid or completely liquid
+ BuildWorldPhysModel( collisionList[i], NO_SHRINK, VPHYSICS_MERGE);
+ }
+ else
+ {
+ ConvertModelToPhysCollide( collisionList[i], i, MASK_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|MASK_WATER, VPHYSICS_SHRINK, VPHYSICS_MERGE );
+ }
+
+ pTextBuffer[i] = NULL;
+ if ( !collisionList[i].Count() )
+ continue;
+
+ // if we've got collision models, write their script for processing in the game
+ pTextBuffer[i] = new CTextBuffer;
+ for ( j = 0; j < collisionList[i].Count(); j++ )
+ {
+ // dump a text file for visualization
+ if ( dumpcollide )
+ {
+ collisionList[i][j]->DumpCollide( pTextBuffer[i], i, j );
+ }
+ // each model knows how to write its script
+ collisionList[i][j]->WriteToTextBuffer( pTextBuffer[i], i, j );
+ // total up the binary section's size
+ totalSize += collisionList[i][j]->GetCollisionBinarySize() + sizeof(int);
+ }
+
+ // These sections only appear in the world's collision text
+ if ( i == 0 )
+ {
+ if ( !g_bNoVirtualMesh && physcollision->SupportsVirtualMesh() )
+ {
+ pTextBuffer[i]->WriteText("virtualterrain {}\n");
+ }
+ if ( s_WorldPropList.Count() )
+ {
+ pTextBuffer[i]->WriteText( "materialtable {\n" );
+ for ( j = 0; j < s_WorldPropList.Count(); j++ )
+ {
+ int propIndex = s_WorldPropList[j];
+ if ( propIndex < 0 )
+ {
+ pTextBuffer[i]->WriteIntKey( "default", j+1 );
+ }
+ else
+ {
+ pTextBuffer[i]->WriteIntKey( physprops->GetPropName( propIndex ), j+1 );
+ }
+ }
+ pTextBuffer[i]->WriteText( "}\n" );
+ }
+ }
+
+ pTextBuffer[i]->Terminate();
+
+ // total lump size includes the text buffers (scripts)
+ totalSize += pTextBuffer[i]->GetSize();
+
+ physModelCount++;
+ }
+
+ // add one for tail of list marker
+ physModelCount++;
+
+ // DWORD align the lump because AddLump assumes that it is DWORD aligned.
+ byte *ptr ;
+ g_PhysCollideSize = totalSize + (physModelCount * sizeof(dphysmodel_t));
+ g_pPhysCollide = (byte *)malloc(( g_PhysCollideSize + 3 ) & ~3 );
+ memset( g_pPhysCollide, 0, g_PhysCollideSize );
+ ptr = g_pPhysCollide;
+
+ for ( i = 0; i < nummodels; i++ )
+ {
+ if ( pTextBuffer[i] )
+ {
+ int j;
+
+ dphysmodel_t model;
+
+ model.modelIndex = i;
+ model.solidCount = collisionList[i].Count();
+ model.dataSize = sizeof(int) * model.solidCount;
+
+ for ( j = 0; j < model.solidCount; j++ )
+ {
+ model.dataSize += collisionList[i][j]->GetCollisionBinarySize();
+ }
+ model.keydataSize = pTextBuffer[i]->GetSize();
+
+ // store the header
+ memcpy( ptr, &model, sizeof(model) );
+ ptr += sizeof(model);
+
+ for ( j = 0; j < model.solidCount; j++ )
+ {
+ int collideSize = collisionList[i][j]->GetCollisionBinarySize();
+
+ // write size
+ memcpy( ptr, &collideSize, sizeof(int) );
+ ptr += sizeof(int);
+
+ // now write the collision model
+ collisionList[i][j]->WriteCollisionBinary( reinterpret_cast<char *>(ptr) );
+ ptr += collideSize;
+ }
+
+ memcpy( ptr, pTextBuffer[i]->GetData(), pTextBuffer[i]->GetSize() );
+ ptr += pTextBuffer[i]->GetSize();
+ }
+
+ delete pTextBuffer[i];
+ }
+
+ dphysmodel_t model;
+
+ // Mark end of list
+ model.modelIndex = -1;
+ model.dataSize = -1;
+ model.keydataSize = 0;
+ model.solidCount = 0;
+ memcpy( ptr, &model, sizeof(model) );
+ ptr += sizeof(model);
+ Assert( (ptr-g_pPhysCollide) == g_PhysCollideSize);
+ Msg("done (%d) (%d bytes)\n", (int)(Plat_FloatTime() - start), g_PhysCollideSize );
+
+ // UNDONE: Collision models (collisionList) memory leak!
+}
diff --git a/mp/src/utils/vbsp/ivp.h b/mp/src/utils/vbsp/ivp.h new file mode 100644 index 00000000..d3c310dc --- /dev/null +++ b/mp/src/utils/vbsp/ivp.h @@ -0,0 +1,75 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef IVP_H
+#define IVP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+
+class CPhysCollide;
+class CTextBuffer;
+class IPhysicsCollision;
+extern IPhysicsCollision *physcollision;
+
+// a list of all of the materials in the world model
+extern int RemapWorldMaterial( int materialIndexIn );
+
+class CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntry( CPhysCollide *pCollide );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex ) = 0;
+
+ unsigned int GetCollisionBinarySize();
+ unsigned int WriteCollisionBinary( char *pDest );
+
+protected:
+ void DumpCollideFileName( const char *pName, int modelIndex, CTextBuffer *pTextBuffer );
+
+protected:
+ CPhysCollide *m_pCollide;
+};
+
+class CPhysCollisionEntryStaticMesh : public CPhysCollisionEntry
+{
+public:
+ CPhysCollisionEntryStaticMesh( CPhysCollide *pCollide, const char *pMaterialName );
+
+ virtual void WriteToTextBuffer( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+ virtual void DumpCollide( CTextBuffer *pTextBuffer, int modelIndex, int collideIndex );
+
+private:
+ const char *m_pMaterial;
+};
+
+
+class CTextBuffer
+{
+public:
+ CTextBuffer( void );
+ ~CTextBuffer( void );
+ inline int GetSize( void ) { return m_buffer.Count(); }
+ inline char *GetData( void ) { return m_buffer.Base(); }
+
+ void WriteText( const char *pText );
+ void WriteIntKey( const char *pKeyName, int outputData );
+ void WriteStringKey( const char *pKeyName, const char *outputData );
+ void WriteFloatKey( const char *pKeyName, float outputData );
+ void WriteFloatArrayKey( const char *pKeyName, const float *outputData, int count );
+
+ void CopyStringQuotes( const char *pString );
+ void Terminate( void );
+private:
+ void CopyData( const char *pData, int len );
+ CUtlVector<char> m_buffer;
+};
+
+#endif // IVP_H
diff --git a/mp/src/utils/vbsp/leakfile.cpp b/mp/src/utils/vbsp/leakfile.cpp new file mode 100644 index 00000000..bd8a9b28 --- /dev/null +++ b/mp/src/utils/vbsp/leakfile.cpp @@ -0,0 +1,168 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "color.h"
+
+/*
+==============================================================================
+
+LEAF FILE GENERATION
+
+Save out name.line for qe3 to read
+==============================================================================
+*/
+
+
+/*
+=============
+LeakFile
+
+Finds the shortest possible chain of portals
+that leads from the outside leaf to a specifically
+occupied leaf
+=============
+*/
+void LeakFile (tree_t *tree)
+{
+ Vector mid;
+ FILE *linefile;
+ char filename[1024];
+ node_t *node;
+ int count;
+
+ if (!tree->outside_node.occupied)
+ return;
+
+ tree->leaked = true;
+ qprintf ("--- LeakFile ---\n");
+
+ //
+ // write the points to the file
+ //
+ sprintf (filename, "%s.lin", source);
+ linefile = fopen (filename, "w");
+ if (!linefile)
+ Error ("Couldn't open %s\n", filename);
+
+ count = 0;
+ node = &tree->outside_node;
+ while (node->occupied > 1)
+ {
+ portal_t *nextportal = NULL;
+ node_t *nextnode = NULL;
+ int s = 0;
+
+ // find the best portal exit
+ int next = node->occupied;
+ for (portal_t *p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (p->nodes[s]->occupied
+ && p->nodes[s]->occupied < next)
+ {
+ nextportal = p;
+ nextnode = p->nodes[s];
+ next = nextnode->occupied;
+ }
+ }
+ node = nextnode;
+ WindingCenter (nextportal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+
+ // Add the occupant's origin to the leakfile.
+ Vector origin;
+ GetVectorForKey (node->occupant, "origin", origin);
+
+ fprintf (linefile, "%f %f %f\n", origin[0], origin[1], origin[2]);
+ qprintf ("%5i point linefile\n", count+1);
+
+ fclose (linefile);
+
+ // Emit a leak warning.
+ const char *cl = ValueForKey (node->occupant, "classname");
+ Color red(255,0,0,255);
+ ColorSpewMessage( SPEW_MESSAGE, &red, "Entity %s (%.2f %.2f %.2f) leaked!\n", cl, origin[0], origin[1], origin[2] );
+}
+
+void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart )
+{
+ Vector mid;
+ FILE *linefile;
+ char filename[1024];
+ node_t *node;
+ int count;
+
+ // wrote a leak line file already, don't overwrite it with the areaportal leak file
+ if ( tree->leaked )
+ return;
+
+ tree->leaked = true;
+ qprintf ("--- LeakFile ---\n");
+
+ //
+ // write the points to the file
+ //
+ sprintf (filename, "%s.lin", source);
+ linefile = fopen (filename, "w");
+ if (!linefile)
+ Error ("Couldn't open %s\n", filename);
+
+ count = 2;
+ WindingCenter (pEndPortal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ mid = 0.5 * (pStart->mins + pStart->maxs);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+
+ node = pStart;
+ while (node->occupied >= 1)
+ {
+ portal_t *nextportal = NULL;
+ node_t *nextnode = NULL;
+ int s = 0;
+
+ // find the best portal exit
+ int next = node->occupied;
+ for (portal_t *p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (p->nodes[s]->occupied
+ && p->nodes[s]->occupied < next)
+ {
+ nextportal = p;
+ nextnode = p->nodes[s];
+ next = nextnode->occupied;
+ }
+ }
+ if ( !nextnode )
+ break;
+ node = nextnode;
+ WindingCenter (nextportal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+ // add the occupant center
+ if ( node )
+ {
+ mid = 0.5 * (node->mins + node->maxs);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+ WindingCenter (pStartPortal->winding, mid);
+ count++;
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+
+ qprintf ("%5i point linefile\n", count);
+
+ fclose (linefile);
+ Warning( "Wrote %s\n", filename );
+ Color red(255,0,0,255);
+ ColorSpewMessage( SPEW_MESSAGE, &red, "Areaportal leak ! File: %s ", filename );
+}
\ No newline at end of file diff --git a/mp/src/utils/vbsp/manifest.cpp b/mp/src/utils/vbsp/manifest.cpp new file mode 100644 index 00000000..44dd07b8 --- /dev/null +++ b/mp/src/utils/vbsp/manifest.cpp @@ -0,0 +1,568 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "vbsp.h"
+#include "map_shared.h"
+#include "fgdlib/fgdlib.h"
+#include "manifest.h"
+#include "windows.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: default constructor
+//-----------------------------------------------------------------------------
+CManifestMap::CManifestMap( void )
+{
+ m_RelativeMapFileName[ 0 ] = 0;
+ m_bTopLevelMap = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: default constructor
+//-----------------------------------------------------------------------------
+CManifest::CManifest( void )
+{
+ m_InstancePath[ 0 ] = 0;
+ m_bIsCordoning = false;
+ m_CordoningMapEnt = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will parse through the known keys for the manifest map entry
+// Input : szKey - the key name
+// szValue - the value
+// pManifestMap - the manifest map this belongs to
+// Output : ChunkFileResult_t - result of the parsing
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap )
+{
+ if ( !stricmp( szKey, "Name" ) )
+ {
+ // pManifestMap->m_FriendlyName = szValue;
+ }
+ else if ( !stricmp( szKey, "File" ) )
+ {
+ strcpy( pManifestMap->m_RelativeMapFileName, szValue );
+ }
+ else if ( !stricmp( szKey, "IsPrimary" ) )
+ {
+ // pManifestMap->m_bPrimaryMap = ( atoi( szValue ) == 1 );
+ }
+ else if ( !stricmp( szKey, "IsProtected" ) )
+ {
+ // pManifestMap->m_bCanBeModified = ( atoi( szValue ) != 1 );
+ }
+ else if ( !stricmp( szKey, "TopLevel" ) )
+ {
+ pManifestMap->m_bTopLevelMap = ( atoi( szValue ) == 1 );
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function is responsible for setting up the manifest map about to be read in
+// Input : pFile - the chunk file being read
+// pDoc - the owning manifest document
+// Output : ChunkFileResult_t - result of the parsing
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ CManifestMap *pManifestMap = new CManifestMap();
+
+ pManifest->m_Maps.AddToTail( pManifestMap );
+
+ ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadManifestMapKeyCallback, pManifestMap );
+
+ return( eResult );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will load the VMF chunk
+// Input : pFile - the chunk file being read
+// pDoc - the owning manifest document
+// Output : ChunkFileResult_t - result of the parsing
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "VMF", ( ChunkHandler_t )LoadManifestVMFCallback, pManifest );
+ pFile->PushHandlers(&Handlers);
+
+ ChunkFileResult_t eResult = ChunkFile_Ok;
+
+ eResult = pFile->ReadChunk();
+
+ pFile->PopHandlers();
+
+ return( eResult );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon )
+{
+ // Add a box to this cordon.
+ pCordon->m_Boxes.AddToTail();
+ BoundBox &box = pCordon->m_Boxes.Tail();
+
+ // Fill it in with the data from the VMF.
+ return pFile->ReadChunk( (KeyHandler_t)LoadCordonBoxKeyCallback, (void *)&box );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox )
+{
+ if (!stricmp(szKey, "mins"))
+ {
+ CChunkFile::ReadKeyValuePoint(szValue, pBox->bmins);
+ }
+ else if (!stricmp(szKey, "maxs"))
+ {
+ CChunkFile::ReadKeyValuePoint(szValue, pBox->bmaxs);
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon )
+{
+ if (!stricmp(szKey, "name"))
+ {
+ pCordon->m_szName.Set( szValue );
+ }
+ // Whether this particular cordon volume is active.
+ else if (!stricmp(szKey, "active"))
+ {
+ CChunkFile::ReadKeyValueBool(szValue, pCordon->m_bActive);
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ // Add a new cordon which will be filled in by the key callback
+ pManifest->m_Cordons.AddToTail();
+ Cordon_t &cordon = pManifest->m_Cordons.Tail();
+
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "box", (ChunkHandler_t)CManifest::LoadCordonBoxCallback, (void *)&cordon );
+
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonKeyCallback, (void *)&cordon );
+ pFile->PopHandlers();
+
+ return(eResult);
+}
+
+
+//-----------------------------------------------------------------------------------------------------------
+// Parses keys that are applicable to all cordons in the map.
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonsKeyCallback( const char *szKey, const char *szValue, CManifest *pManifest )
+{
+ // Whether the cordoning system is enabled or disabled.
+ if ( !stricmp( szKey, "active" ) )
+ {
+ CChunkFile::ReadKeyValueBool( szValue, pManifest->m_bIsCordoning );
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+//-----------------------------------------------------------------------------
+// Parses the VMF chunk that pertains to all the cordons in the map:
+//
+// cordons
+// {
+// "active" "true"
+// cordon
+// {
+// "active" "true"
+// "box"
+// {
+// "mins" "-1024, -1024, -1024"
+// "maxs" "1024, 1024, 1024"
+// }
+// ...may be more boxes...
+// }
+// ...may be more cordons...
+// }
+//
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CManifest::LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest )
+{
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "cordon", (ChunkHandler_t)CManifest::LoadCordonCallback, pManifest );
+
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk( (KeyHandler_t)LoadCordonsKeyCallback, pManifest );
+ pFile->PopHandlers();
+
+ return(eResult);
+}
+
+extern ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+
+ChunkFileResult_t CManifest::LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pDoc )
+{
+ pDoc->m_CordoningMapEnt = &g_MainMap->entities[g_MainMap->num_entities];
+ g_MainMap->num_entities++;
+ memset( pDoc->m_CordoningMapEnt, 0, sizeof( *pDoc->m_CordoningMapEnt ) );
+ pDoc->m_CordoningMapEnt->firstbrush = g_MainMap->nummapbrushes;
+ pDoc->m_CordoningMapEnt->numbrushes = 0;
+
+ LoadEntity_t LoadEntity;
+ LoadEntity.pEntity = pDoc->m_CordoningMapEnt;
+
+ // No default flags/contents
+ LoadEntity.nBaseFlags = 0;
+ LoadEntity.nBaseContents = 0;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "cordons", ( ChunkHandler_t )CManifest::LoadCordonsCallback, pDoc );
+ Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
+ pFile->PushHandlers(&Handlers);
+
+ ChunkFileResult_t eResult = ChunkFile_Ok;
+
+ eResult = pFile->ReadChunk();
+
+ pFile->PopHandlers();
+
+ return( eResult );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will create a new entity pair
+// Input : pKey - the key of the pair
+// pValue - the value of the pair
+// Output : returns a newly created epair structure
+//-----------------------------------------------------------------------------
+epair_t *CManifest::CreateEPair( char *pKey, char *pValue )
+{
+ epair_t *pEPair = new epair_t;
+
+ pEPair->key = new char[ strlen( pKey ) + 1 ];
+ pEPair->value = new char[ strlen( pValue ) + 1 ];
+
+ strcpy( pEPair->key, pKey );
+ strcpy( pEPair->value, pValue );
+
+ return pEPair;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will load in all of the submaps belonging to this manifest,
+// except for the top level map, which is loaded separately.
+// Input : pMapFile - the top level map that was previously loaded
+// pszFileName - the absolute file name of the top level map file
+// Output : returns true if all submaps were loaded
+//-----------------------------------------------------------------------------
+bool CManifest::LoadSubMaps( CMapFile *pMapFile, const char *pszFileName )
+{
+ entity_t *InstanceEntity;
+ epair_t *pEPair;
+
+ InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
+ pMapFile->num_entities++;
+ memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
+
+ InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
+ pEPair = CreateEPair( "classname", "worldspawn" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ for( int i = 0; i < m_Maps.Count(); i++ )
+ {
+ // if ( m_Maps[ i ]->m_bTopLevelMap == false )
+ {
+ char FileName[ MAX_PATH ];
+
+ sprintf( FileName, "%s%s", m_InstancePath, m_Maps[ i ]->m_RelativeMapFileName );
+
+ InstanceEntity = &pMapFile->entities[ pMapFile->num_entities ];
+ pMapFile->num_entities++;
+
+ memset( InstanceEntity, 0, sizeof( *InstanceEntity ) );
+ InstanceEntity->origin.Init( 0.0f, 0.0f, 0.0f );
+
+ pEPair = CreateEPair( "angles", "0 0 0" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ char temp[ 128 ];
+ sprintf( temp, "%d", GameData::NAME_FIXUP_NONE );
+
+ pEPair = CreateEPair( "fixup_style", temp );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ pEPair = CreateEPair( "classname", "func_instance" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ pEPair = CreateEPair( "file", m_Maps[ i ]->m_RelativeMapFileName );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+
+ if ( m_Maps[ i ]->m_bTopLevelMap == true )
+ {
+ pEPair = CreateEPair( "toplevel", "1" );
+ pEPair->next = InstanceEntity->epairs;
+ InstanceEntity->epairs = pEPair;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CManifest::LoadVMFManifestUserPrefs( const char *pszFileName )
+{
+ char UserName[ MAX_PATH ], FileName[ MAX_PATH ], UserPrefsFileName[ MAX_PATH ];
+ DWORD UserNameSize;
+
+ UserNameSize = sizeof( UserName );
+ if ( GetUserName( UserName, &UserNameSize ) == 0 )
+ {
+ strcpy( UserPrefsFileName, "default" );
+ }
+
+ sprintf( UserPrefsFileName, "\\%s.vmm_prefs", UserName );
+ V_StripExtension( pszFileName, FileName, sizeof( FileName ) );
+ strcat( FileName, UserPrefsFileName );
+
+ FILE *fp = fopen( FileName, "rb" );
+ if ( !fp )
+ {
+ return false;
+ }
+
+ CChunkFile File;
+ ChunkFileResult_t eResult = File.Open( FileName, ChunkFile_Read );
+
+ if ( eResult == ChunkFile_Ok )
+ {
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "cordoning", ( ChunkHandler_t )CManifest::LoadManifestCordoningPrefsCallback, this );
+
+ // Handlers.SetErrorHandler( ( ChunkErrorHandler_t )CMapDoc::HandleLoadError, this);
+
+ File.PushHandlers(&Handlers);
+
+ while( eResult == ChunkFile_Ok )
+ {
+ eResult = File.ReadChunk();
+ }
+
+ if ( eResult == ChunkFile_EOF )
+ {
+ eResult = ChunkFile_Ok;
+ }
+
+ File.PopHandlers();
+ }
+
+ if ( eResult == ChunkFile_Ok )
+ {
+ }
+ else
+ {
+ // no pref message for now
+ // GetMainWnd()->MessageBox( File.GetErrorText( eResult ), "Error loading manifest!", MB_OK | MB_ICONEXCLAMATION );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads a .VMM file.
+// Input : pszFileName - Full path of the map file to load.
+//-----------------------------------------------------------------------------
+bool CManifest::LoadVMFManifest( const char *pszFileName )
+{
+ V_StripExtension( pszFileName, m_InstancePath, sizeof( m_InstancePath ) );
+ strcat( m_InstancePath, "\\" );
+
+ CChunkFile File;
+ ChunkFileResult_t eResult = File.Open( pszFileName, ChunkFile_Read );
+ if ( eResult != ChunkFile_Ok )
+ {
+ g_MapError.ReportError( File.GetErrorText( eResult ) );
+ return false;
+ }
+
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "Maps", ( ChunkHandler_t )LoadManifestMapsCallback, this );
+
+ File.PushHandlers(&Handlers);
+
+ while (eResult == ChunkFile_Ok)
+ {
+ eResult = File.ReadChunk();
+ }
+
+ if (eResult == ChunkFile_EOF)
+ {
+ eResult = ChunkFile_Ok;
+ }
+
+ File.PopHandlers();
+
+ if ( eResult == ChunkFile_Ok )
+ {
+ int index = g_Maps.AddToTail( new CMapFile() );
+ g_LoadingMap = g_Maps[ index ];
+ if ( g_MainMap == NULL )
+ {
+ g_MainMap = g_LoadingMap;
+ }
+
+ LoadSubMaps( g_LoadingMap, pszFileName );
+
+ LoadVMFManifestUserPrefs( pszFileName );
+ }
+
+ return ( eResult == ChunkFile_Ok );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CManifest::CordonWorld( )
+{
+ if ( m_bIsCordoning == false )
+ {
+ return;
+ }
+
+ for ( int i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ if ( i == 0 )
+ { // for world spawn, we look at brushes
+ for( int nBrushNum = 0; nBrushNum < g_MainMap->entities[ i ].numbrushes; nBrushNum++ )
+ {
+ int nIndex = g_MainMap->entities[ i ].firstbrush + nBrushNum;
+
+ bool bRemove = true;
+
+ for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
+ {
+ if ( m_Cordons[ nCordon ].m_bActive == false )
+ {
+ continue;
+ }
+
+ for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
+ {
+ if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].IsIntersectingBox( g_MainMap->mapbrushes[ nIndex ].mins, g_MainMap->mapbrushes[ nIndex ].maxs ) == true )
+ {
+ bRemove = false;
+ break;
+ }
+ }
+
+ if ( bRemove == false )
+ {
+ break;
+ }
+ }
+
+ if ( bRemove )
+ {
+ int nSize = ( g_MainMap->entities[ i ].numbrushes - nBrushNum - 1 ) * sizeof( g_MainMap->mapbrushes[ 0 ] );
+ memmove( &g_MainMap->mapbrushes[ nIndex ], &g_MainMap->mapbrushes[ nIndex + 1 ], nSize );
+ g_MainMap->entities[ i ].numbrushes--;
+ nBrushNum--;
+ }
+ }
+ }
+ else if ( &g_MainMap->entities[ i ] != m_CordoningMapEnt )
+ { // for all other entities, even if they include brushes, we look at origin
+ if ( g_MainMap->entities[ i ].numbrushes == 0 && g_MainMap->entities[ i ].epairs == NULL )
+ {
+ continue;
+ }
+
+ bool bRemove = true;
+
+ for( int nCordon = 0; nCordon < m_Cordons.Count(); nCordon++ )
+ {
+ if ( m_Cordons[ nCordon ].m_bActive == false )
+ {
+ continue;
+ }
+
+ for( int nBox = 0; nBox < m_Cordons[ nCordon ].m_Boxes.Count(); nBox++ )
+ {
+ if ( m_Cordons[ nCordon ].m_Boxes[ nBox ].ContainsPoint( g_MainMap->entities[ i ].origin ) == true )
+ {
+ bRemove = false;
+ break;
+ }
+ }
+
+ if ( bRemove == false )
+ {
+ break;
+ }
+ }
+
+ if ( bRemove )
+ {
+ g_MainMap->entities[ i ].numbrushes = 0;
+ g_MainMap->entities[ i ].epairs = NULL;
+ }
+ }
+ }
+
+ if ( m_CordoningMapEnt )
+ {
+ g_MainMap->MoveBrushesToWorldGeneral( m_CordoningMapEnt );
+ m_CordoningMapEnt->numbrushes = 0;
+ m_CordoningMapEnt->epairs = NULL;
+ }
+}
diff --git a/mp/src/utils/vbsp/manifest.h b/mp/src/utils/vbsp/manifest.h new file mode 100644 index 00000000..f3b915a1 --- /dev/null +++ b/mp/src/utils/vbsp/manifest.h @@ -0,0 +1,73 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=====================================================================================//
+
+#ifndef __MANIFEST_H
+#define __MANIFEST_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "boundbox.h"
+
+//
+// Each cordon is a named collection of bounding boxes.
+//
+struct Cordon_t
+{
+ inline Cordon_t()
+ {
+ m_bActive = false;
+ }
+
+ CUtlString m_szName;
+ bool m_bActive; // True means cull using this cordon when cordoning is enabled.
+ CUtlVector<BoundBox> m_Boxes;
+};
+
+class CManifestMap
+{
+public:
+ CManifestMap( void );
+ char m_RelativeMapFileName[ MAX_PATH ];
+ bool m_bTopLevelMap;
+};
+
+class CManifest
+{
+public:
+ CManifest( void );
+
+ static ChunkFileResult_t LoadManifestMapKeyCallback( const char *szKey, const char *szValue, CManifestMap *pManifestMap );
+ static ChunkFileResult_t LoadManifestVMFCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadManifestMapsCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadCordonBoxCallback( CChunkFile *pFile, Cordon_t *pCordon );
+ static ChunkFileResult_t LoadCordonBoxKeyCallback( const char *szKey, const char *szValue, BoundBox *pBox );
+ static ChunkFileResult_t LoadCordonKeyCallback( const char *szKey, const char *szValue, Cordon_t *pCordon );
+ static ChunkFileResult_t LoadCordonCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadCordonsKeyCallback( const char *pszKey, const char *pszValue, CManifest *pManifest );
+ static ChunkFileResult_t LoadCordonsCallback( CChunkFile *pFile, CManifest *pManifest );
+ static ChunkFileResult_t LoadManifestCordoningPrefsCallback( CChunkFile *pFile, CManifest *pManifest );
+
+ bool LoadSubMaps( CMapFile *pMapFile, const char *pszFileName );
+ epair_t *CreateEPair( char *pKey, char *pValue );
+ bool LoadVMFManifest( const char *pszFileName );
+ const char *GetInstancePath( ) { return m_InstancePath; }
+
+ void CordonWorld( );
+
+private:
+ bool LoadVMFManifestUserPrefs( const char *pszFileName );
+
+
+ CUtlVector< CManifestMap * > m_Maps;
+ char m_InstancePath[ MAX_PATH ];
+ bool m_bIsCordoning;
+ CUtlVector< Cordon_t > m_Cordons;
+ entity_t *m_CordoningMapEnt;
+};
+
+#endif // #ifndef __MANIFEST_H
diff --git a/mp/src/utils/vbsp/map.cpp b/mp/src/utils/vbsp/map.cpp new file mode 100644 index 00000000..34219bd4 --- /dev/null +++ b/mp/src/utils/vbsp/map.cpp @@ -0,0 +1,3304 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "map_shared.h"
+#include "disp_vbsp.h"
+#include "tier1/strtools.h"
+#include "builddisp.h"
+#include "tier0/icommandline.h"
+#include "KeyValues.h"
+#include "materialsub.h"
+#include "fgdlib/fgdlib.h"
+#include "manifest.h"
+
+#ifdef VSVMFIO
+#include "VmfImport.h"
+#endif // VSVMFIO
+
+
+// undefine to make plane finding use linear sort
+#define USE_HASHING
+
+#define RENDER_NORMAL_EPSILON 0.00001
+#define RENDER_DIST_EPSILON 0.01f
+
+#define BRUSH_CLIP_EPSILON 0.01f // this should probably be the same
+ // as clip epsilon, but it is 0.1f and I
+ // currently don't know how that number was
+ // come to (cab) - this is 0.01 of an inch
+ // for clipping brush solids
+struct LoadSide_t
+{
+ mapbrush_t *pBrush;
+ side_t *pSide;
+ int nSideIndex;
+ int nBaseFlags;
+ int nBaseContents;
+ Vector planepts[3];
+ brush_texture_t td;
+};
+
+
+extern qboolean onlyents;
+
+
+CUtlVector< CMapFile * > g_Maps;
+CMapFile *g_MainMap = NULL;
+CMapFile *g_LoadingMap = NULL;
+
+char CMapFile::m_InstancePath[ MAX_PATH ] = "";
+int CMapFile::m_InstanceCount = 0;
+int CMapFile::c_areaportals = 0;
+
+void CMapFile::Init( void )
+{
+ entity_num = 0;
+ num_entities = 0;
+
+ nummapplanes = 0;
+ memset( mapplanes, 0, sizeof( mapplanes ) );
+
+ nummapbrushes = 0;
+ memset( mapbrushes, 0, sizeof( mapbrushes ) );
+
+ nummapbrushsides = 0;
+ memset( brushsides, 0, sizeof( brushsides ) );
+
+ memset( side_brushtextures, 0, sizeof( side_brushtextures ) );
+
+ memset( planehash, 0, sizeof( planehash ) );
+
+ m_ConnectionPairs = NULL;
+
+ m_StartMapOverlays = g_aMapOverlays.Count();
+ m_StartMapWaterOverlays = g_aMapWaterOverlays.Count();
+
+ c_boxbevels = 0;
+ c_edgebevels = 0;
+ c_clipbrushes = 0;
+ g_ClipTexinfo = -1;
+}
+
+
+// All the brush sides referenced by info_no_dynamic_shadow entities.
+CUtlVector<int> g_NoDynamicShadowSides;
+
+
+void TestExpandBrushes (void);
+
+ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo );
+ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+
+#ifdef VSVMFIO
+ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo);
+ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo);
+#endif // VSVMFIO
+
+ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
+ChunkFileResult_t LoadEntityKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
+
+ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
+
+ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush);
+
+ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
+ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo);
+
+
+
+/*
+=============================================================================
+
+PLANE FINDING
+
+=============================================================================
+*/
+
+
+/*
+=================
+PlaneTypeForNormal
+=================
+*/
+int PlaneTypeForNormal (Vector& normal)
+{
+ vec_t ax, ay, az;
+
+// NOTE: should these have an epsilon around 1.0?
+ if (normal[0] == 1.0 || normal[0] == -1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0 || normal[1] == -1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0 || normal[2] == -1.0)
+ return PLANE_Z;
+
+ ax = fabs(normal[0]);
+ ay = fabs(normal[1]);
+ az = fabs(normal[2]);
+
+ if (ax >= ay && ax >= az)
+ return PLANE_ANYX;
+ if (ay >= ax && ay >= az)
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+}
+
+/*
+================
+PlaneEqual
+================
+*/
+qboolean PlaneEqual (plane_t *p, Vector& normal, vec_t dist, float normalEpsilon, float distEpsilon)
+{
+#if 1
+ if (
+ fabs(p->normal[0] - normal[0]) < normalEpsilon
+ && fabs(p->normal[1] - normal[1]) < normalEpsilon
+ && fabs(p->normal[2] - normal[2]) < normalEpsilon
+ && fabs(p->dist - dist) < distEpsilon )
+ return true;
+#else
+ if (p->normal[0] == normal[0]
+ && p->normal[1] == normal[1]
+ && p->normal[2] == normal[2]
+ && p->dist == dist)
+ return true;
+#endif
+ return false;
+}
+
+/*
+================
+AddPlaneToHash
+================
+*/
+void CMapFile::AddPlaneToHash (plane_t *p)
+{
+ int hash;
+
+ hash = (int)fabs(p->dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ p->hash_chain = planehash[hash];
+ planehash[hash] = p;
+}
+
+/*
+================
+CreateNewFloatPlane
+================
+*/
+int CMapFile::CreateNewFloatPlane (Vector& normal, vec_t dist)
+{
+ plane_t *p, temp;
+
+ if (VectorLength(normal) < 0.5)
+ g_MapError.ReportError ("FloatPlane: bad normal");
+ // create a new plane
+ if (nummapplanes+2 > MAX_MAP_PLANES)
+ g_MapError.ReportError ("MAX_MAP_PLANES");
+
+ p = &mapplanes[nummapplanes];
+ VectorCopy (normal, p->normal);
+ p->dist = dist;
+ p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
+
+ VectorSubtract (vec3_origin, normal, (p+1)->normal);
+ (p+1)->dist = -dist;
+
+ nummapplanes += 2;
+
+ // allways put axial planes facing positive first
+ if (p->type < 3)
+ {
+ if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
+ {
+ // flip order
+ temp = *p;
+ *p = *(p+1);
+ *(p+1) = temp;
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 1;
+ }
+ }
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 2;
+}
+
+
+/*
+==============
+SnapVector
+==============
+*/
+bool SnapVector (Vector& normal)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(normal[i] - 1) < RENDER_NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ return true;
+ }
+
+ if ( fabs(normal[i] - -1) < RENDER_NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = -1;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
+// Rounds dist to integer if it is within an epsilon of integer.
+// Input : normal - Plane normal vector (assumed to be unit length).
+// dist - Plane constant.
+//-----------------------------------------------------------------------------
+void SnapPlane(Vector &normal, vec_t &dist)
+{
+ SnapVector(normal);
+
+ if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
+ {
+ dist = RoundInt(dist);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Snaps normal to axis-aligned if it is within an epsilon of axial.
+// Recalculates dist if the normal was snapped. Rounds dist to integer
+// if it is within an epsilon of integer.
+// Input : normal - Plane normal vector (assumed to be unit length).
+// dist - Plane constant.
+// p0, p1, p2 - Three points on the plane.
+//-----------------------------------------------------------------------------
+void SnapPlane(Vector &normal, vec_t &dist, const Vector &p0, const Vector &p1, const Vector &p2)
+{
+ if (SnapVector(normal))
+ {
+ //
+ // Calculate a new plane constant using the snapped normal. Use the
+ // centroid of the three plane points to minimize error. This is like
+ // rotating the plane around the centroid.
+ //
+ Vector p3 = (p0 + p1 + p2) / 3.0f;
+ dist = normal.Dot(p3);
+ if ( g_snapAxialPlanes )
+ {
+ dist = RoundInt(dist);
+ }
+ }
+
+ if (fabs(dist - RoundInt(dist)) < RENDER_DIST_EPSILON)
+ {
+ dist = RoundInt(dist);
+ }
+}
+
+
+/*
+=============
+FindFloatPlane
+
+=============
+*/
+#ifndef USE_HASHING
+int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+
+ SnapPlane(normal, dist);
+ for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
+ {
+ if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
+ return i;
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#else
+int CMapFile::FindFloatPlane (Vector& normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+ int hash, h;
+
+ SnapPlane(normal, dist);
+ hash = (int)fabs(dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ // search the border bins as well
+ for (i=-1 ; i<=1 ; i++)
+ {
+ h = (hash+i)&(PLANE_HASHES-1);
+ for (p = planehash[h] ; p ; p=p->hash_chain)
+ {
+ if (PlaneEqual (p, normal, dist, RENDER_NORMAL_EPSILON, RENDER_DIST_EPSILON))
+ return p-mapplanes;
+ }
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Builds a plane normal and distance from three points on the plane.
+// If the normal is nearly axial, it will be snapped to be axial. Looks
+// up the plane in the unique planes.
+// Input : p0, p1, p2 - Three points on the plane.
+// Output : Returns the index of the plane in the planes list.
+//-----------------------------------------------------------------------------
+int CMapFile::PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2)
+{
+ Vector t1, t2, normal;
+ vec_t dist;
+
+ VectorSubtract (p0, p1, t1);
+ VectorSubtract (p2, p1, t2);
+ CrossProduct (t1, t2, normal);
+ VectorNormalize (normal);
+
+ dist = DotProduct (p0, normal);
+
+ SnapPlane(normal, dist, p0, p1, p2);
+
+ return FindFloatPlane (normal, dist);
+}
+
+
+/*
+===========
+BrushContents
+===========
+*/
+int BrushContents (mapbrush_t *b)
+{
+ int contents;
+ int unionContents = 0;
+ side_t *s;
+ int i;
+
+ s = &b->original_sides[0];
+ contents = s->contents;
+ unionContents = contents;
+ for (i=1 ; i<b->numsides ; i++, s++)
+ {
+ s = &b->original_sides[i];
+
+ unionContents |= s->contents;
+#if 0
+ if (s->contents != contents)
+ {
+ Msg("Brush %i: mixed face contents\n", b->id);
+ break;
+ }
+#endif
+ }
+
+ // NOTE: we're making slime translucent so that it doesn't block lighting on things floating on its surface
+ int transparentContents = unionContents & (CONTENTS_WINDOW|CONTENTS_GRATE|CONTENTS_WATER|CONTENTS_SLIME);
+ if ( transparentContents )
+ {
+ contents |= transparentContents | CONTENTS_TRANSLUCENT;
+ contents &= ~CONTENTS_SOLID;
+ }
+
+ return contents;
+}
+
+
+//============================================================================
+
+bool IsAreaPortal( char const *pClassName )
+{
+ // If the class name starts with "func_areaportal", then it's considered an area portal.
+ char const *pBaseName = "func_areaportal";
+ char const *pCur = pBaseName;
+ while( *pCur && *pClassName )
+ {
+ if( *pCur != *pClassName )
+ break;
+
+ ++pCur;
+ ++pClassName;
+ }
+
+ return *pCur == 0;
+}
+
+
+/*
+=================
+AddBrushBevels
+
+Adds any additional planes necessary to allow the brush to be expanded
+against axial bounding boxes
+=================
+*/
+void CMapFile::AddBrushBevels (mapbrush_t *b)
+{
+ int axis, dir;
+ int i, j, k, l, order;
+ side_t sidetemp;
+ brush_texture_t tdtemp;
+ side_t *s, *s2;
+ Vector normal;
+ float dist;
+ winding_t *w, *w2;
+ Vector vec, vec2;
+ float d;
+
+ //
+ // add the axial planes
+ //
+ order = 0;
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2, order++)
+ {
+ // see if the plane is allready present
+ for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
+ {
+ if (mapplanes[s->planenum].normal[axis] == dir)
+ break;
+ }
+
+ if (i == b->numsides)
+ { // add a new side
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ b->numsides++;
+ VectorClear (normal);
+ normal[axis] = dir;
+ if (dir == 1)
+ dist = b->maxs[axis];
+ else
+ dist = -b->mins[axis];
+ s->planenum = FindFloatPlane (normal, dist);
+ s->texinfo = b->original_sides[0].texinfo;
+ s->contents = b->original_sides[0].contents;
+ s->bevel = true;
+ c_boxbevels++;
+ }
+
+ // if the plane is not in it canonical order, swap it
+ if (i != order)
+ {
+ sidetemp = b->original_sides[order];
+ b->original_sides[order] = b->original_sides[i];
+ b->original_sides[i] = sidetemp;
+
+ j = b->original_sides - brushsides;
+ tdtemp = side_brushtextures[j+order];
+ side_brushtextures[j+order] = side_brushtextures[j+i];
+ side_brushtextures[j+i] = tdtemp;
+ }
+ }
+ }
+
+ //
+ // add the edge bevels
+ //
+ if (b->numsides == 6)
+ return; // pure axial
+
+ // test the non-axial plane edges
+ for (i=6 ; i<b->numsides ; i++)
+ {
+ s = b->original_sides + i;
+ w = s->winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ k = (j+1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[k], vec);
+ if (VectorNormalize (vec) < 0.5)
+ continue;
+ SnapVector (vec);
+ for (k=0 ; k<3 ; k++)
+ if ( vec[k] == -1 || vec[k] == 1)
+ break; // axial
+ if (k != 3)
+ continue; // only test non-axial edges
+
+ // try the six possible slanted axials from this edge
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2)
+ {
+ // construct a plane
+ VectorClear (vec2);
+ vec2[axis] = dir;
+ CrossProduct (vec, vec2, normal);
+ if (VectorNormalize (normal) < 0.5)
+ continue;
+ dist = DotProduct (w->p[j], normal);
+
+ // if all the points on all the sides are
+ // behind this plane, it is a proper edge bevel
+ for (k=0 ; k<b->numsides ; k++)
+ {
+ // if this plane has allready been used, skip it
+ // NOTE: Use a larger tolerance for collision planes than for rendering planes
+ if ( PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist, 0.01f, 0.01f ) )
+ break;
+
+ w2 = b->original_sides[k].winding;
+ if (!w2)
+ continue;
+ for (l=0 ; l<w2->numpoints ; l++)
+ {
+ d = DotProduct (w2->p[l], normal) - dist;
+ if (d > 0.1)
+ break; // point in front
+ }
+ if (l != w2->numpoints)
+ break;
+ }
+
+ if (k != b->numsides)
+ continue; // wasn't part of the outer hull
+ // add this plane
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ nummapbrushsides++;
+ s2 = &b->original_sides[b->numsides];
+ s2->planenum = FindFloatPlane (normal, dist);
+ s2->texinfo = b->original_sides[0].texinfo;
+ s2->contents = b->original_sides[0].contents;
+ s2->bevel = true;
+ c_edgebevels++;
+ b->numsides++;
+ }
+ }
+ }
+ }
+}
+
+/*
+================
+MakeBrushWindings
+
+makes basewindigs for sides and mins / maxs for the brush
+================
+*/
+qboolean CMapFile::MakeBrushWindings (mapbrush_t *ob)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ ClearBounds (ob->mins, ob->maxs);
+
+ for (i=0 ; i<ob->numsides ; i++)
+ {
+ plane = &mapplanes[ob->original_sides[i].planenum];
+ w = BaseWindingForPlane (plane->normal, plane->dist);
+ for (j=0 ; j<ob->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ if (ob->original_sides[j].bevel)
+ continue;
+ plane = &mapplanes[ob->original_sides[j].planenum^1];
+// ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+ // adding an epsilon here, due to precision issues creating complex
+ // displacement surfaces (cab)
+ ChopWindingInPlace( &w, plane->normal, plane->dist, BRUSH_CLIP_EPSILON );
+ }
+
+ side = &ob->original_sides[i];
+ side->winding = w;
+ if (w)
+ {
+ side->visible = true;
+ for (j=0 ; j<w->numpoints ; j++)
+ AddPointToBounds (w->p[j], ob->mins, ob->maxs);
+ }
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (ob->mins[i] < MIN_COORD_INTEGER || ob->maxs[i] > MAX_COORD_INTEGER)
+ Msg("Brush %i: bounds out of range\n", ob->id);
+ if (ob->mins[i] > MAX_COORD_INTEGER || ob->maxs[i] < MIN_COORD_INTEGER)
+ Msg("Brush %i: no visible sides on brush\n", ob->id);
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes all of the brushes from the current entity and adds them to the
+// world's brush list. Used by func_detail and func_areaportal.
+// THIS ROUTINE MAY ONLY BE USED DURING ENTITY LOADING.
+// Input : mapent - Entity whose brushes are to be moved to the world.
+//-----------------------------------------------------------------------------
+void CMapFile::MoveBrushesToWorld( entity_t *mapent )
+{
+ int newbrushes;
+ int worldbrushes;
+ mapbrush_t *temp;
+ int i;
+
+ // this is pretty gross, because the brushes are expected to be
+ // in linear order for each entity
+
+ newbrushes = mapent->numbrushes;
+ worldbrushes = entities[0].numbrushes;
+
+ temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
+ memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
+
+#if 0 // let them keep their original brush numbers
+ for (i=0 ; i<newbrushes ; i++)
+ temp[i].entitynum = 0;
+#endif
+
+ // make space to move the brushes (overlapped copy)
+ memmove (mapbrushes + worldbrushes + newbrushes,
+ mapbrushes + worldbrushes,
+ sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes) );
+
+ // copy the new brushes down
+ memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
+
+ // fix up indexes
+ entities[0].numbrushes += newbrushes;
+ for (i=1 ; i<num_entities ; i++)
+ entities[i].firstbrush += newbrushes;
+ free (temp);
+
+ mapent->numbrushes = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes all of the brushes from the current entity and adds them to the
+// world's brush list. Used by func_detail and func_areaportal.
+// Input : mapent - Entity whose brushes are to be moved to the world.
+//-----------------------------------------------------------------------------
+void CMapFile::MoveBrushesToWorldGeneral( entity_t *mapent )
+{
+ int newbrushes;
+ int worldbrushes;
+ mapbrush_t *temp;
+ int i;
+
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ if ( mapdispinfo[ i ].entitynum == ( mapent - entities ) )
+ {
+ mapdispinfo[ i ].entitynum = 0;
+ }
+ }
+
+ // this is pretty gross, because the brushes are expected to be
+ // in linear order for each entity
+ newbrushes = mapent->numbrushes;
+ worldbrushes = entities[0].numbrushes;
+
+ temp = (mapbrush_t *)malloc(newbrushes*sizeof(mapbrush_t));
+ memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
+
+#if 0 // let them keep their original brush numbers
+ for (i=0 ; i<newbrushes ; i++)
+ temp[i].entitynum = 0;
+#endif
+
+ // make space to move the brushes (overlapped copy)
+ memmove (mapbrushes + worldbrushes + newbrushes,
+ mapbrushes + worldbrushes,
+ sizeof(mapbrush_t) * (mapent->firstbrush - worldbrushes) );
+
+
+ // wwwxxxmmyyy
+
+ // copy the new brushes down
+ memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
+
+ // fix up indexes
+ entities[0].numbrushes += newbrushes;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ if ( entities[ i ].firstbrush < mapent->firstbrush ) // if we use <=, then we'll remap the passed in ent, which we don't want to
+ {
+ entities[ i ].firstbrush += newbrushes;
+ }
+ }
+ free (temp);
+
+ mapent->numbrushes = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates the sides of brush and removed CONTENTS_DETAIL from each side
+// Input : *brush -
+//-----------------------------------------------------------------------------
+void RemoveContentsDetailFromBrush( mapbrush_t *brush )
+{
+ // Only valid on non-world brushes
+ Assert( brush->entitynum != 0 );
+
+ side_t *s;
+ int i;
+
+ s = &brush->original_sides[0];
+ for ( i=0 ; i<brush->numsides ; i++, s++ )
+ {
+ if ( s->contents & CONTENTS_DETAIL )
+ {
+ s->contents &= ~CONTENTS_DETAIL;
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates all brushes in an entity and removes CONTENTS_DETAIL from all brushes
+// Input : *mapent -
+//-----------------------------------------------------------------------------
+void CMapFile::RemoveContentsDetailFromEntity( entity_t *mapent )
+{
+ int i;
+ for ( i = 0; i < mapent->numbrushes; i++ )
+ {
+ int brushnum = mapent->firstbrush + i;
+
+ mapbrush_t *brush = &mapbrushes[ brushnum ];
+ RemoveContentsDetailFromBrush( brush );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFile -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispDistancesCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : szKey -
+// szValue -
+// pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext = strtok(szBuf, " ");
+ int nIndex = nRow * nCols;
+
+ while (pszNext != NULL)
+ {
+ pMapDispInfo->dispDists[nIndex] = (float)atof(pszNext);
+ pszNext = strtok(NULL, " ");
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: load in the displacement info "chunk" from the .map file into the
+// vbsp map displacement info data structure
+// Output : return the index of the map displacement info
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispInfoCallback(CChunkFile *pFile, mapdispinfo_t **ppMapDispInfo )
+{
+ //
+ // check to see if we exceeded the maximum displacement info list size
+ //
+ if (nummapdispinfo > MAX_MAP_DISPINFO)
+ {
+ g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO" );
+ }
+
+ // get a pointer to the next available displacement info slot
+ mapdispinfo_t *pMapDispInfo = &mapdispinfo[nummapdispinfo];
+ nummapdispinfo++;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, pMapDispInfo);
+ Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, pMapDispInfo);
+ Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, pMapDispInfo);
+ Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, pMapDispInfo);
+ Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, pMapDispInfo);
+
+#ifdef VSVMFIO
+ Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, pMapDispInfo);
+#endif // VSVMFIO
+
+ //
+ // Read the displacement chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispInfoKeyCallback, pMapDispInfo);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ // return a pointer to the displacement info
+ *ppMapDispInfo = pMapDispInfo;
+ }
+
+ return(eResult);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *mapent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispInfoKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!stricmp(szKey, "power"))
+ {
+ CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->power);
+ }
+#ifdef VSVMFIO
+ else if (!stricmp(szKey, "elevation"))
+ {
+ CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->m_elevation);
+ }
+#endif // VSVMFIO
+ else if (!stricmp(szKey, "uaxis"))
+ {
+ CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->uAxis);
+ }
+ else if (!stricmp(szKey, "vaxis"))
+ {
+ CChunkFile::ReadKeyValueVector3(szValue, pMapDispInfo->vAxis);
+ }
+ else if( !stricmp( szKey, "startposition" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pMapDispInfo->startPosition );
+ }
+ else if( !stricmp( szKey, "flags" ) )
+ {
+ CChunkFile::ReadKeyValueInt( szValue, pMapDispInfo->flags );
+ }
+#if 0 // old data
+ else if (!stricmp( szKey, "alpha" ) )
+ {
+ CChunkFile::ReadKeyValueVector4( szValue, pMapDispInfo->alphaValues );
+ }
+#endif
+ else if (!stricmp(szKey, "mintess"))
+ {
+ CChunkFile::ReadKeyValueInt(szValue, pMapDispInfo->minTess);
+ }
+ else if (!stricmp(szKey, "smooth"))
+ {
+ CChunkFile::ReadKeyValueFloat(szValue, pMapDispInfo->smoothingAngle);
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFile -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+ char *pszNext1 = strtok(NULL, " ");
+ char *pszNext2 = strtok(NULL, " ");
+
+ int nIndex = nRow * nCols;
+
+ while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
+ {
+ pMapDispInfo->vectorDisps[nIndex][0] = (float)atof(pszNext0);
+ pMapDispInfo->vectorDisps[nIndex][1] = (float)atof(pszNext1);
+ pMapDispInfo->vectorDisps[nIndex][2] = (float)atof(pszNext2);
+
+ pszNext0 = strtok(NULL, " ");
+ pszNext1 = strtok(NULL, " ");
+ pszNext2 = strtok(NULL, " ");
+
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispOffsetsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+ char *pszNext1 = strtok(NULL, " ");
+ char *pszNext2 = strtok(NULL, " ");
+
+ int nIndex = nRow * nCols;
+
+ while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
+ {
+ pMapDispInfo->vectorOffsets[nIndex][0] = (float)atof(pszNext0);
+ pMapDispInfo->vectorOffsets[nIndex][1] = (float)atof(pszNext1);
+ pMapDispInfo->vectorOffsets[nIndex][2] = (float)atof(pszNext2);
+
+ pszNext0 = strtok(NULL, " ");
+ pszNext1 = strtok(NULL, " ");
+ pszNext2 = strtok(NULL, " ");
+
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+#ifdef VSVMFIO
+ChunkFileResult_t LoadDispOffsetNormalsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pMapDispInfo));
+}
+
+
+ChunkFileResult_t LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+ char *pszNext1 = strtok(NULL, " ");
+ char *pszNext2 = strtok(NULL, " ");
+
+ int nIndex = nRow * nCols;
+
+ while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
+ {
+ pMapDispInfo->m_offsetNormals[nIndex][0] = (float)atof(pszNext0);
+ pMapDispInfo->m_offsetNormals[nIndex][1] = (float)atof(pszNext1);
+ pMapDispInfo->m_offsetNormals[nIndex][2] = (float)atof(pszNext2);
+
+ pszNext0 = strtok(NULL, " ");
+ pszNext1 = strtok(NULL, " ");
+ pszNext2 = strtok(NULL, " ");
+
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+#endif // VSVMFIO
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispAlphasCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pMapDispInfo));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *szKey -
+// *szValue -
+// *pDisp -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if (!strnicmp(szKey, "row", 3))
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy(szBuf, szValue);
+
+ int nCols = (1 << pMapDispInfo->power) + 1;
+ int nRow = atoi(&szKey[3]);
+
+ char *pszNext0 = strtok(szBuf, " ");
+
+ int nIndex = nRow * nCols;
+
+ while (pszNext0 != NULL)
+ {
+ pMapDispInfo->alphaValues[nIndex] = (float)atof(pszNext0);
+ pszNext0 = strtok(NULL, " ");
+ nIndex++;
+ }
+ }
+
+ return(ChunkFile_Ok);
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispTriangleTagsCallback(CChunkFile *pFile, mapdispinfo_t *pMapDispInfo)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pMapDispInfo));
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, mapdispinfo_t *pMapDispInfo)
+{
+ if ( !strnicmp( szKey, "row", 3 ) )
+ {
+ char szBuf[MAX_KEYVALUE_LEN];
+ strcpy( szBuf, szValue );
+
+ int nCols = ( 1 << pMapDispInfo->power );
+ int nRow = atoi( &szKey[3] );
+
+ char *pszNext = strtok( szBuf, " " );
+
+ int nIndex = nRow * nCols;
+ int iTri = nIndex * 2;
+
+ while ( pszNext != NULL )
+ {
+ // Collapse the tags here!
+ unsigned short nTriTags = ( unsigned short )atoi( pszNext );
+
+ // Walkable
+ bool bWalkable = ( ( nTriTags & COREDISPTRI_TAG_WALKABLE ) != 0 );
+ if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) != 0 ) )
+ {
+ bWalkable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_WALKABLE_VAL ) != 0 );
+ }
+
+ // Buildable
+ bool bBuildable = ( ( nTriTags & COREDISPTRI_TAG_BUILDABLE ) != 0 );
+ if ( ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) != 0 ) )
+ {
+ bBuildable = ( ( nTriTags & COREDISPTRI_TAG_FORCE_BUILDABLE_VAL ) != 0 );
+ }
+
+ nTriTags = 0;
+ if ( bWalkable )
+ {
+ nTriTags |= DISPTRI_TAG_WALKABLE;
+ }
+
+ if ( bBuildable )
+ {
+ nTriTags |= DISPTRI_TAG_BUILDABLE;
+ }
+
+ pMapDispInfo->triTags[iTri] = nTriTags;
+ pszNext = strtok( NULL, " " );
+ iTri++;
+ }
+ }
+
+ return( ChunkFile_Ok );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : brushSideID -
+// Output : int
+//-----------------------------------------------------------------------------
+int CMapFile::SideIDToIndex( int brushSideID )
+{
+ int i;
+ for ( i = 0; i < nummapbrushsides; i++ )
+ {
+ if ( brushsides[i].id == brushSideID )
+ {
+ return i;
+ }
+ }
+ Assert( 0 );
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *mapent -
+// *key -
+//-----------------------------------------------------------------------------
+void ConvertSideList( entity_t *mapent, char *key )
+{
+ char *pszSideList = ValueForKey( mapent, key );
+
+ if (pszSideList)
+ {
+ char *pszTmpList = ( char* )_alloca( strlen( pszSideList ) + 1 );
+ strcpy( pszTmpList, pszSideList );
+
+ bool bFirst = true;
+ char szNewValue[1024];
+ szNewValue[0] = '\0';
+
+ const char *pszScan = strtok( pszTmpList, " " );
+ if ( !pszScan )
+ return;
+ do
+ {
+ int nSideID;
+
+ if ( sscanf( pszScan, "%d", &nSideID ) == 1 )
+ {
+ int nIndex = g_LoadingMap->SideIDToIndex(nSideID);
+ if (nIndex != -1)
+ {
+ if (!bFirst)
+ {
+ strcat( szNewValue, " " );
+ }
+ else
+ {
+ bFirst = false;
+ }
+
+ char szIndex[15];
+ itoa( nIndex, szIndex, 10 );
+ strcat( szNewValue, szIndex );
+ }
+ }
+ } while ( ( pszScan = strtok( NULL, " " ) ) );
+
+ SetKeyValue( mapent, key, szNewValue );
+ }
+}
+
+
+// Add all the sides referenced by info_no_dynamic_shadows entities to g_NoDynamicShadowSides.
+ChunkFileResult_t HandleNoDynamicShadowsEnt( entity_t *pMapEnt )
+{
+ // Get the list of the sides.
+ char *pSideList = ValueForKey( pMapEnt, "sides" );
+
+ // Parse the side list.
+ char *pScan = strtok( pSideList, " " );
+ if( pScan )
+ {
+ do
+ {
+ int brushSideID;
+ if( sscanf( pScan, "%d", &brushSideID ) == 1 )
+ {
+ if ( g_NoDynamicShadowSides.Find( brushSideID ) == -1 )
+ g_NoDynamicShadowSides.AddToTail( brushSideID );
+ }
+ } while( ( pScan = strtok( NULL, " " ) ) );
+ }
+
+ // Clear out this entity.
+ pMapEnt->epairs = NULL;
+ return ( ChunkFile_Ok );
+}
+
+
+static ChunkFileResult_t LoadOverlayDataTransitionKeyCallback( const char *szKey, const char *szValue, mapoverlay_t *pOverlay )
+{
+ if ( !stricmp( szKey, "material" ) )
+ {
+ // Get the material name.
+ const char *pMaterialName = szValue;
+ if( g_ReplaceMaterials )
+ {
+ pMaterialName = ReplaceMaterialName( szValue );
+ }
+
+ Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
+ if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
+ {
+ Error( "Overlay Material Name (%s) > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
+ return ChunkFile_Fail;
+ }
+ strcpy( pOverlay->szMaterialName, pMaterialName );
+ }
+ else if ( !stricmp( szKey, "StartU") )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[0] );
+ }
+ else if ( !stricmp( szKey, "EndU" ) )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flU[1] );
+ }
+ else if ( !stricmp( szKey, "StartV" ) )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[0] );
+ }
+ else if ( !stricmp( szKey, "EndV" ) )
+ {
+ CChunkFile::ReadKeyValueFloat( szValue, pOverlay->flV[1] );
+ }
+ else if ( !stricmp( szKey, "BasisOrigin" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecOrigin );
+ }
+ else if ( !stricmp( szKey, "BasisU" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[0] );
+ }
+ else if ( !stricmp( szKey, "BasisV" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[1] );
+ }
+ else if ( !stricmp( szKey, "BasisNormal" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecBasis[2] );
+ }
+ else if ( !stricmp( szKey, "uv0" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[0] );
+ }
+ else if ( !stricmp( szKey, "uv1" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[1] );
+ }
+ else if ( !stricmp( szKey, "uv2" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[2] );
+ }
+ else if ( !stricmp( szKey, "uv3" ) )
+ {
+ CChunkFile::ReadKeyValueVector3( szValue, pOverlay->vecUVPoints[3] );
+ }
+ else if ( !stricmp( szKey, "sides" ) )
+ {
+ const char *pSideList = szValue;
+ char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
+ strcpy( pTmpList, pSideList );
+ const char *pScan = strtok( pTmpList, " " );
+ if ( !pScan )
+ return ChunkFile_Fail;
+
+ pOverlay->aSideList.Purge();
+ pOverlay->aFaceList.Purge();
+
+ do
+ {
+ int nSideId;
+ if ( sscanf( pScan, "%d", &nSideId ) == 1 )
+ {
+ pOverlay->aSideList.AddToTail( nSideId );
+ }
+ } while ( ( pScan = strtok( NULL, " " ) ) );
+ }
+
+ return ChunkFile_Ok;
+}
+
+static ChunkFileResult_t LoadOverlayDataTransitionCallback( CChunkFile *pFile, int nParam )
+{
+ int iOverlay = g_aMapWaterOverlays.AddToTail();
+ mapoverlay_t *pOverlay = &g_aMapWaterOverlays[iOverlay];
+ if ( !pOverlay )
+ return ChunkFile_Fail;
+
+ pOverlay->nId = ( MAX_MAP_OVERLAYS + 1 ) + g_aMapWaterOverlays.Count() - 1;
+ pOverlay->m_nRenderOrder = 0;
+
+ ChunkFileResult_t eResult = pFile->ReadChunk( ( KeyHandler_t )LoadOverlayDataTransitionKeyCallback, pOverlay );
+ return eResult;
+}
+
+static ChunkFileResult_t LoadOverlayTransitionCallback( CChunkFile *pFile, int nParam )
+{
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "overlaydata", ( ChunkHandler_t )LoadOverlayDataTransitionCallback, 0 );
+ pFile->PushHandlers( &Handlers );
+
+ ChunkFileResult_t eResult = pFile->ReadChunk( NULL, NULL );
+
+ pFile->PopHandlers();
+
+ return eResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Iterates all brushes in a ladder entity, generates its mins and maxs.
+// These are stored in the object, since the brushes are going to go away.
+// Input : *mapent -
+//-----------------------------------------------------------------------------
+void CMapFile::AddLadderKeys( entity_t *mapent )
+{
+ Vector mins, maxs;
+ ClearBounds( mins, maxs );
+
+ int i;
+ for ( i = 0; i < mapent->numbrushes; i++ )
+ {
+ int brushnum = mapent->firstbrush + i;
+ mapbrush_t *brush = &mapbrushes[ brushnum ];
+
+ AddPointToBounds( brush->mins, mins, maxs );
+ AddPointToBounds( brush->maxs, mins, maxs );
+ }
+
+ char buf[16];
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", mins.x );
+ SetKeyValue( mapent, "mins.x", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", mins.y );
+ SetKeyValue( mapent, "mins.y", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", mins.z );
+ SetKeyValue( mapent, "mins.z", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.x );
+ SetKeyValue( mapent, "maxs.x", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.y );
+ SetKeyValue( mapent, "maxs.y", buf );
+
+ Q_snprintf( buf, sizeof(buf), "%2.2f", maxs.z );
+ SetKeyValue( mapent, "maxs.z", buf );
+}
+
+ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam)
+{
+ return g_LoadingMap->LoadEntityCallback( pFile, nParam );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pFile -
+// ulParam -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CMapFile::LoadEntityCallback(CChunkFile *pFile, int nParam)
+{
+ if (num_entities == MAX_MAP_ENTITIES)
+ {
+ // Exits.
+ g_MapError.ReportError ("num_entities == MAX_MAP_ENTITIES");
+ }
+
+ entity_t *mapent = &entities[num_entities];
+ num_entities++;
+ memset(mapent, 0, sizeof(*mapent));
+ mapent->firstbrush = nummapbrushes;
+ mapent->numbrushes = 0;
+ //mapent->portalareas[0] = -1;
+ //mapent->portalareas[1] = -1;
+
+ LoadEntity_t LoadEntity;
+ LoadEntity.pEntity = mapent;
+
+ // No default flags/contents
+ LoadEntity.nBaseFlags = 0;
+ LoadEntity.nBaseContents = 0;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("solid", (ChunkHandler_t)::LoadSolidCallback, &LoadEntity);
+ Handlers.AddHandler("connections", (ChunkHandler_t)LoadConnectionsCallback, &LoadEntity);
+ Handlers.AddHandler( "overlaytransition", ( ChunkHandler_t )LoadOverlayTransitionCallback, 0 );
+
+ //
+ // Read the entity chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadEntityKeyCallback, &LoadEntity);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ GetVectorForKey (mapent, "origin", mapent->origin);
+
+ const char *pMinDXLevelStr = ValueForKey( mapent, "mindxlevel" );
+ const char *pMaxDXLevelStr = ValueForKey( mapent, "maxdxlevel" );
+ if( *pMinDXLevelStr != '\0' || *pMaxDXLevelStr != '\0' )
+ {
+ int min = 0;
+ int max = 0;
+ if( *pMinDXLevelStr )
+ {
+ min = atoi( pMinDXLevelStr );
+ }
+ if( *pMaxDXLevelStr )
+ {
+ max = atoi( pMaxDXLevelStr );
+ }
+
+ // Set min and max to default values.
+ if( min == 0 )
+ {
+ min = g_nDXLevel;
+ }
+ if( max == 0 )
+ {
+ max = g_nDXLevel;
+ }
+ if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < min || g_nDXLevel > max ) )
+ {
+ mapent->numbrushes = 0;
+ mapent->epairs = NULL;
+ return(ChunkFile_Ok);
+ }
+ }
+
+ // offset all of the planes and texinfo
+ if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] )
+ {
+ for (int i=0 ; i<mapent->numbrushes ; i++)
+ {
+ mapbrush_t *b = &mapbrushes[mapent->firstbrush + i];
+ for (int j=0 ; j<b->numsides ; j++)
+ {
+ side_t *s = &b->original_sides[j];
+ vec_t newdist = mapplanes[s->planenum].dist - DotProduct (mapplanes[s->planenum].normal, mapent->origin);
+ s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
+ if ( !onlyents )
+ {
+ s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], &side_brushtextures[s-brushsides], mapent->origin);
+ }
+ }
+ MakeBrushWindings (b);
+ }
+ }
+
+ //
+ // func_detail brushes are moved into the world entity. The CONTENTS_DETAIL flag was set by the loader.
+ //
+ const char *pClassName = ValueForKey( mapent, "classname" );
+
+ if ( !strcmp( "func_detail", pClassName ) )
+ {
+ MoveBrushesToWorld (mapent);
+ mapent->numbrushes = 0;
+
+ // clear out this entity
+ mapent->epairs = NULL;
+ return(ChunkFile_Ok);
+ }
+
+ // these get added to a list for processing the portal file
+ // but aren't necessary to emit to the BSP
+ if ( !strcmp( "func_viscluster", pClassName ) )
+ {
+ AddVisCluster(mapent);
+ return(ChunkFile_Ok);
+ }
+
+ //
+ // func_ladder brushes are moved into the world entity. We convert the func_ladder to an info_ladder
+ // that holds the ladder's mins and maxs, and leave the entity. This helps the bots figure out ladders.
+ //
+ if ( !strcmp( "func_ladder", pClassName ) )
+ {
+ AddLadderKeys( mapent );
+
+ MoveBrushesToWorld (mapent);
+
+ // Convert to info_ladder entity
+ SetKeyValue( mapent, "classname", "info_ladder" );
+
+ return(ChunkFile_Ok);
+ }
+
+ if( !strcmp( "env_cubemap", pClassName ) )
+ {
+ if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
+ {
+ const char *pSideListStr = ValueForKey( mapent, "sides" );
+ int size;
+ size = IntForKey( mapent, "cubemapsize" );
+ Cubemap_InsertSample( mapent->origin, size );
+ Cubemap_SaveBrushSides( pSideListStr );
+ }
+ // clear out this entity
+ mapent->epairs = NULL;
+ return(ChunkFile_Ok);
+ }
+
+ if ( !strcmp( "test_sidelist", pClassName ) )
+ {
+ ConvertSideList(mapent, "sides");
+ return ChunkFile_Ok;
+ }
+
+ if ( !strcmp( "info_overlay", pClassName ) )
+ {
+ int iAccessorID = Overlay_GetFromEntity( mapent );
+
+ if ( iAccessorID < 0 )
+ {
+ // Clear out this entity.
+ mapent->epairs = NULL;
+ }
+ else
+ {
+ // Convert to info_overlay_accessor entity
+ SetKeyValue( mapent, "classname", "info_overlay_accessor" );
+
+ // Remember the id for accessing the overlay
+ char buf[16];
+ Q_snprintf( buf, sizeof(buf), "%i", iAccessorID );
+ SetKeyValue( mapent, "OverlayID", buf );
+ }
+
+ return ( ChunkFile_Ok );
+ }
+
+ if ( !strcmp( "info_overlay_transition", pClassName ) )
+ {
+ // Clear out this entity.
+ mapent->epairs = NULL;
+ return ( ChunkFile_Ok );
+ }
+
+ if ( Q_stricmp( pClassName, "info_no_dynamic_shadow" ) == 0 )
+ {
+ return HandleNoDynamicShadowsEnt( mapent );
+ }
+
+ if ( Q_stricmp( pClassName, "func_instance_parms" ) == 0 )
+ {
+ // Clear out this entity.
+ mapent->epairs = NULL;
+ return ( ChunkFile_Ok );
+ }
+
+ // areaportal entities move their brushes, but don't eliminate
+ // the entity
+ if( IsAreaPortal( pClassName ) )
+ {
+ char str[128];
+
+ if (mapent->numbrushes != 1)
+ {
+ Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
+ }
+
+ mapbrush_t *b = &mapbrushes[nummapbrushes-1];
+ b->contents = CONTENTS_AREAPORTAL;
+ c_areaportals++;
+ mapent->areaportalnum = c_areaportals;
+
+ // set the portal number as "portalnumber"
+ sprintf (str, "%i", c_areaportals);
+ SetKeyValue (mapent, "portalnumber", str);
+
+ MoveBrushesToWorld (mapent);
+ return(ChunkFile_Ok);
+ }
+
+#ifdef VSVMFIO
+ if ( !Q_stricmp( pClassName, "light" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_lightHDR" ),
+ ValueForKey( mapent, "_lightscaleHDR" ),
+ ValueForKey( mapent, "_quadratic_attn" ) );
+ }
+
+ if ( !Q_stricmp( pClassName, "light_spot" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightSpotCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "pitch" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_lightHDR" ),
+ ValueForKey( mapent, "_lightscaleHDR" ),
+ ValueForKey( mapent, "_quadratic_attn" ),
+ ValueForKey( mapent, "_inner_cone" ),
+ ValueForKey( mapent, "_cone" ),
+ ValueForKey( mapent, "_exponent" ) );
+ }
+
+ if ( !Q_stricmp( pClassName, "light_dynamic" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightDynamicCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "pitch" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_quadratic_attn" ),
+ ValueForKey( mapent, "_inner_cone" ),
+ ValueForKey( mapent, "_cone" ),
+ ValueForKey( mapent, "brightness" ),
+ ValueForKey( mapent, "distance" ),
+ ValueForKey( mapent, "spotlight_radius" ) );
+ }
+
+ if ( !Q_stricmp( pClassName, "light_environment" ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportLightEnvironmentCallback(
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "origin" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "pitch" ),
+ ValueForKey( mapent, "_light" ),
+ ValueForKey( mapent, "_lightHDR" ),
+ ValueForKey( mapent, "_lightscaleHDR" ),
+ ValueForKey( mapent, "_ambient" ),
+ ValueForKey( mapent, "_ambientHDR" ),
+ ValueForKey( mapent, "_AmbientScaleHDR" ),
+ ValueForKey( mapent, "SunSpreadAngle" ) );
+ }
+
+ const char *pModel = ValueForKey( mapent, "model" );
+ if ( pModel && Q_strlen( pModel ) )
+ {
+ CVmfImport::GetVmfImporter()->ImportModelCallback(
+ pModel,
+ ValueForKey( mapent, "hammerid" ),
+ ValueForKey( mapent, "angles" ),
+ ValueForKey( mapent, "origin" ),
+ MDagPath() );
+ }
+#endif // VSVMFIO
+
+ // If it's not in the world at this point, unmark CONTENTS_DETAIL from all sides...
+ if ( mapent != &entities[ 0 ] )
+ {
+ RemoveContentsDetailFromEntity( mapent );
+ }
+
+ return(ChunkFile_Ok);
+ }
+
+ return(eResult);
+}
+
+
+entity_t* EntityByName( char const *pTestName )
+{
+ if( !pTestName )
+ return 0;
+
+ for( int i=0; i < g_MainMap->num_entities; i++ )
+ {
+ entity_t *e = &g_MainMap->entities[i];
+
+ const char *pName = ValueForKey( e, "targetname" );
+ if( stricmp( pName, pTestName ) == 0 )
+ return e;
+ }
+
+ return 0;
+}
+
+
+void CMapFile::ForceFuncAreaPortalWindowContents()
+{
+ // Now go through all areaportal entities and force CONTENTS_WINDOW
+ // on the brushes of the bmodels they point at.
+ char *targets[] = {"target", "BackgroundBModel"};
+ int nTargets = sizeof(targets) / sizeof(targets[0]);
+
+ for( int i=0; i < num_entities; i++ )
+ {
+ entity_t *e = &entities[i];
+
+ const char *pClassName = ValueForKey( e, "classname" );
+
+ // Don't do this on "normal" func_areaportal entities. Those are tied to doors
+ // and should be opaque when closed. But areaportal windows (and any other
+ // distance-based areaportals) should be windows because they are normally open/transparent
+ if( !IsAreaPortal( pClassName ) || !Q_stricmp( pClassName, "func_areaportal" ) )
+ continue;
+
+// const char *pTestEntName = ValueForKey( e, "targetname" );
+
+ for( int iTarget=0; iTarget < nTargets; iTarget++ )
+ {
+ char const *pEntName = ValueForKey( e, targets[iTarget] );
+ if( !pEntName[0] )
+ continue;
+
+ entity_t *pBrushEnt = EntityByName( pEntName );
+ if( !pBrushEnt )
+ continue;
+
+ for( int iBrush=0; iBrush < pBrushEnt->numbrushes; iBrush++ )
+ {
+ mapbrushes[pBrushEnt->firstbrush + iBrush].contents &= ~CONTENTS_SOLID;
+ mapbrushes[pBrushEnt->firstbrush + iBrush].contents |= CONTENTS_TRANSLUCENT | CONTENTS_WINDOW;
+ }
+ }
+ }
+}
+
+
+// ============ Instancing ============
+
+// #define MERGE_INSTANCE_DEBUG_INFO 1
+
+#define INSTANCE_VARIABLE_KEY "replace"
+
+static GameData GD;
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will read in a standard key / value file
+// Input : pFilename - the absolute name of the file to read
+// Output : returns the KeyValues of the file, NULL if the file could not be read.
+//-----------------------------------------------------------------------------
+static KeyValues *ReadKeyValuesFile( const char *pFilename )
+{
+ // Read in the gameinfo.txt file and null-terminate it.
+ FILE *fp = fopen( pFilename, "rb" );
+ if ( !fp )
+ return NULL;
+ CUtlVector<char> buf;
+ fseek( fp, 0, SEEK_END );
+ buf.SetSize( ftell( fp ) + 1 );
+ fseek( fp, 0, SEEK_SET );
+ fread( buf.Base(), 1, buf.Count()-1, fp );
+ fclose( fp );
+ buf[buf.Count()-1] = 0;
+
+ KeyValues *kv = new KeyValues( "" );
+ if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
+ {
+ kv->deleteThis();
+ return NULL;
+ }
+
+ return kv;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will set a secondary lookup path for instances.
+// Input : pszInstancePath - the secondary lookup path
+//-----------------------------------------------------------------------------
+void CMapFile::SetInstancePath( const char *pszInstancePath )
+{
+ strcpy( m_InstancePath, pszInstancePath );
+ V_strlower( m_InstancePath );
+ V_FixSlashes( m_InstancePath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: This function will attempt to find a full path given the base and relative names.
+// Input : pszBaseFileName - the base file that referenced this instance
+// pszInstanceFileName - the relative file name of this instance
+// Output : Returns true if it was able to locate the file
+// pszOutFileName - the full path to the file name if located
+//-----------------------------------------------------------------------------
+bool CMapFile::DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName )
+{
+ char szInstanceFileNameFixed[ MAX_PATH ];
+ const char *pszMapPath = "\\maps\\";
+
+ strcpy( szInstanceFileNameFixed, pszInstanceFileName );
+ V_SetExtension( szInstanceFileNameFixed, ".vmf", sizeof( szInstanceFileNameFixed ) );
+ V_FixSlashes( szInstanceFileNameFixed );
+
+ // first, try to find a relative location based upon the Base file name
+ strcpy( pszOutFileName, pszBaseFileName );
+ V_StripFilename( pszOutFileName );
+
+ strcat( pszOutFileName, "\\" );
+ strcat( pszOutFileName, szInstanceFileNameFixed );
+
+ if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
+ {
+ return true;
+ }
+
+ // second, try to find the master 'maps' directory and make it relative from that
+ strcpy( pszOutFileName, pszBaseFileName );
+ V_StripFilename( pszOutFileName );
+ V_RemoveDotSlashes( pszOutFileName );
+ V_FixDoubleSlashes( pszOutFileName );
+ V_strlower( pszOutFileName );
+ strcat( pszOutFileName, "\\" );
+
+ char *pos = strstr( pszOutFileName, pszMapPath );
+ if ( pos )
+ {
+ pos += strlen( pszMapPath );
+ *pos = 0;
+ strcat( pszOutFileName, szInstanceFileNameFixed );
+
+ if ( g_pFullFileSystem->FileExists( pszOutFileName ) )
+ {
+ return true;
+ }
+ }
+
+ if ( m_InstancePath[ 0 ] != 0 )
+ {
+ sprintf( szInstanceFileNameFixed, "%s%s", m_InstancePath, pszInstanceFileName );
+
+ if ( g_pFullFileSystem->FileExists( szInstanceFileNameFixed, "GAME" ) )
+ {
+ char FullPath[ MAX_PATH ];
+ g_pFullFileSystem->RelativePathToFullPath( szInstanceFileNameFixed, "GAME", FullPath, sizeof( FullPath ) );
+ strcpy( pszOutFileName, FullPath );
+
+ return true;
+ }
+ }
+
+ pszOutFileName[ 0 ] = 0;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will check the main map for any func_instances. It will
+// also attempt to load in the gamedata file for instancing remapping help.
+// Input : none
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::CheckForInstances( const char *pszFileName )
+{
+ if ( this != g_MainMap )
+ { // all sub-instances will be appended to the main map master list as they are read in
+ // so the main loop below will naturally get to the appended ones.
+ return;
+ }
+
+ char GameInfoPath[ MAX_PATH ];
+
+ g_pFullFileSystem->RelativePathToFullPath( "gameinfo.txt", "MOD", GameInfoPath, sizeof( GameInfoPath ) );
+ KeyValues *GameInfoKV = ReadKeyValuesFile( GameInfoPath );
+ if ( !GameInfoKV )
+ {
+ Msg( "Could not locate gameinfo.txt for Instance Remapping at %s\n", GameInfoPath );
+ return;
+ }
+
+ const char *InstancePath = GameInfoKV->GetString( "InstancePath", NULL );
+ if ( InstancePath )
+ {
+ CMapFile::SetInstancePath( InstancePath );
+ }
+
+ const char *GameDataFile = GameInfoKV->GetString( "GameData", NULL );
+ if ( !GameDataFile )
+ {
+ Msg( "Could not locate 'GameData' key in %s\n", GameInfoPath );
+ return;
+ }
+
+ char FDGPath[ MAX_PATH ];
+ if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "EXECUTABLE_PATH", FDGPath, sizeof( FDGPath ) ) )
+ {
+ if ( !g_pFullFileSystem->RelativePathToFullPath( GameDataFile, "", FDGPath, sizeof( FDGPath ) ) )
+ {
+ Msg( "Could not locate GameData file %s\n", GameDataFile );
+ }
+ }
+
+ GD.Load( FDGPath );
+
+ // this list will grow as instances are merged onto it. sub-instances are merged and
+ // automatically done in this processing.
+ for ( int i = 0; i < num_entities; i++ )
+ {
+ char *pEntity = ValueForKey( &entities[ i ], "classname" );
+ if ( !strcmp( pEntity, "func_instance" ) )
+ {
+ char *pInstanceFile = ValueForKey( &entities[ i ], "file" );
+ if ( pInstanceFile[ 0 ] )
+ {
+ char InstancePath[ MAX_PATH ];
+ bool bLoaded = false;
+
+ if ( DeterminePath( pszFileName, pInstanceFile, InstancePath ) )
+ {
+ if ( LoadMapFile( InstancePath ) )
+ {
+ MergeInstance( &entities[ i ], g_LoadingMap );
+ delete g_LoadingMap;
+ bLoaded = true;
+ }
+ }
+
+ if ( bLoaded == false )
+ {
+ Color red( 255, 0, 0, 255 );
+
+ ColorSpewMessage( SPEW_ERROR, &red, "Could not open instance file %s\n", pInstanceFile );
+ }
+ }
+
+ entities[ i ].numbrushes = 0;
+ entities[ i ].epairs = NULL;
+ }
+ }
+
+ g_LoadingMap = this;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will do all of the necessary work to merge the instance
+// into the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance )
+{
+ matrix3x4_t mat;
+ QAngle angles;
+ Vector OriginOffset = pInstanceEntity->origin;
+
+ m_InstanceCount++;
+
+ GetAnglesForKey( pInstanceEntity, "angles", angles );
+ AngleMatrix( angles, OriginOffset, mat );
+
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( "Instance Remapping: O:( %g, %g, %g ) A:( %g, %g, %g )\n", OriginOffset.x, OriginOffset.y, OriginOffset.z, angles.x, angles.y, angles.z );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ MergePlanes( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeBrushes( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeBrushSides( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeEntities( pInstanceEntity, Instance, OriginOffset, angles, mat );
+ MergeOverlays( pInstanceEntity, Instance, OriginOffset, angles, mat );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the map planes from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ // Each pair of planes needs to be added to the main map
+ for ( int i = 0; i < Instance->nummapplanes; i += 2 )
+ {
+ FindFloatPlane( Instance->mapplanes[i].normal, Instance->mapplanes[i].dist );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the map brushes from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ int max_brush_id = 0;
+
+ for( int i = 0; i < nummapbrushes; i++ )
+ {
+ if ( mapbrushes[ i ].id > max_brush_id )
+ {
+ max_brush_id = mapbrushes[ i ].id;
+ }
+ }
+
+ for( int i = 0; i < Instance->nummapbrushes; i++ )
+ {
+ mapbrushes[ nummapbrushes + i ] = Instance->mapbrushes[ i ];
+
+ mapbrush_t *brush = &mapbrushes[ nummapbrushes + i ];
+ brush->entitynum += num_entities;
+ brush->brushnum += nummapbrushes;
+
+ if ( i < Instance->entities[ 0 ].numbrushes || ( brush->contents & CONTENTS_LADDER ) != 0 )
+ { // world spawn brushes as well as ladders we physically move
+ Vector minsIn = brush->mins;
+ Vector maxsIn = brush->maxs;
+
+ TransformAABB( InstanceMatrix, minsIn, maxsIn, brush->mins, brush->maxs );
+ }
+ else
+ {
+ }
+ brush->id += max_brush_id;
+
+ int index = brush->original_sides - Instance->brushsides;
+ brush->original_sides = &brushsides[ nummapbrushsides + index ];
+ }
+
+ nummapbrushes += Instance->nummapbrushes;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the map sides from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ int max_side_id = 0;
+
+ for( int i = 0; i < nummapbrushsides; i++ )
+ {
+ if ( brushsides[ i ].id > max_side_id )
+ {
+ max_side_id = brushsides[ i ].id;
+ }
+ }
+
+ for( int i = 0; i < Instance->nummapbrushsides; i++ )
+ {
+ brushsides[ nummapbrushsides + i ] = Instance->brushsides[ i ];
+
+ side_t *side = &brushsides[ nummapbrushsides + i ];
+ // The planes got merged & remapped. So you need to search for the output plane index on each side
+ // NOTE: You could optimize this by saving off an index map in MergePlanes
+ side->planenum = FindFloatPlane( Instance->mapplanes[side->planenum].normal, Instance->mapplanes[side->planenum].dist );
+ side->id += max_side_id;
+
+ // this could be pre-processed into a list for quicker checking
+ bool bNeedsTranslation = ( side->pMapDisp && side->pMapDisp->entitynum == 0 );
+ if ( !bNeedsTranslation )
+ { // check for sides that are part of the world spawn - those need translating
+ for( int j = 0; j < Instance->entities[ 0 ].numbrushes; j++ )
+ {
+ int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
+
+ if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) )
+ {
+ bNeedsTranslation = true;
+ break;
+ }
+ }
+ }
+ if ( !bNeedsTranslation )
+ { // sides for ladders are outside of the world spawn, but also need translating
+ for( int j = Instance->entities[ 0 ].numbrushes; j < Instance->nummapbrushes; j++ )
+ {
+ int loc = Instance->mapbrushes[ j ].original_sides - Instance->brushsides;
+
+ if ( i >= loc && i < ( loc + Instance->mapbrushes[ j ].numsides ) && ( Instance->mapbrushes[ j ].contents & CONTENTS_LADDER ) != 0 )
+ {
+ bNeedsTranslation = true;
+ break;
+ }
+ }
+ }
+ if ( bNeedsTranslation )
+ { // we only want to do the adjustment on world spawn brushes, not entity brushes
+ if ( side->winding )
+ {
+ for( int point = 0; point < side->winding->numpoints; point++ )
+ {
+ Vector inPoint = side->winding->p[ point ];
+ VectorTransform( inPoint, InstanceMatrix, side->winding->p[ point ] );
+ }
+ }
+
+ int planenum = side->planenum;
+ cplane_t inPlane, outPlane;
+ inPlane.normal = mapplanes[ planenum ].normal;
+ inPlane.dist = mapplanes[ planenum ].dist;
+
+ MatrixTransformPlane( InstanceMatrix, inPlane, outPlane );
+ planenum = FindFloatPlane( outPlane.normal, outPlane.dist );
+ side->planenum = planenum;
+
+ brush_texture_t bt = Instance->side_brushtextures[ i ];
+
+ VectorRotate( Instance->side_brushtextures[ i ].UAxis, InstanceMatrix, bt.UAxis );
+ VectorRotate( Instance->side_brushtextures[ i ].VAxis, InstanceMatrix, bt.VAxis );
+ bt.shift[ 0 ] -= InstanceOrigin.Dot( bt.UAxis ) / bt.textureWorldUnitsPerTexel[ 0 ];
+ bt.shift[ 1 ] -= InstanceOrigin.Dot( bt.VAxis ) / bt.textureWorldUnitsPerTexel[ 1 ];
+
+ if ( !onlyents )
+ {
+ side->texinfo = TexinfoForBrushTexture ( &mapplanes[ side->planenum ], &bt, vec3_origin );
+ }
+ }
+
+ if ( side->pMapDisp )
+ {
+ mapdispinfo_t *disp = side->pMapDisp;
+
+ disp->brushSideID = side->id;
+ Vector inPoint = disp->startPosition;
+ VectorTransform( inPoint, InstanceMatrix, disp->startPosition );
+
+ disp->face.originalface = side;
+ disp->face.texinfo = side->texinfo;
+ disp->face.planenum = side->planenum;
+ disp->entitynum += num_entities;
+
+ for( int point = 0; point < disp->face.w->numpoints; point++ )
+ {
+ Vector inPoint = disp->face.w->p[ point ];
+ VectorTransform( inPoint, InstanceMatrix, disp->face.w->p[ point ] );
+ }
+
+ }
+ }
+
+ nummapbrushsides += Instance->nummapbrushsides;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will look for replace parameters in the function instance
+// to see if there is anything in the epair that should be replaced.
+// Input : pPair - the epair with the value
+// pInstanceEntity - the func_instance that may ahve replace keywords
+// Output : pPair - the value field may be updated
+//-----------------------------------------------------------------------------
+void CMapFile::ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity )
+{
+ char Value[ MAX_KEYVALUE_LEN ], NewValue[ MAX_KEYVALUE_LEN ];
+ bool Overwritten = false;
+
+ strcpy( NewValue, pPair->value );
+ for ( epair_t *epInstance = pInstanceEntity->epairs; epInstance != NULL; epInstance = epInstance->next )
+ {
+ if ( strnicmp( epInstance->key, INSTANCE_VARIABLE_KEY, strlen( INSTANCE_VARIABLE_KEY ) ) == 0 )
+ {
+ char InstanceVariable[ MAX_KEYVALUE_LEN ];
+
+ strcpy( InstanceVariable, epInstance->value );
+
+ char *ValuePos = strchr( InstanceVariable, ' ' );
+ if ( !ValuePos )
+ {
+ continue;
+ }
+ *ValuePos = 0;
+ ValuePos++;
+
+ strcpy( Value, NewValue );
+ if ( !V_StrSubst( Value, InstanceVariable, ValuePos, NewValue, sizeof( NewValue ), false ) )
+ {
+ Overwritten = true;
+ break;
+ }
+ }
+ }
+
+ if ( !Overwritten && strcmp( pPair->value, NewValue ) != 0 )
+ {
+ free( pPair->value );
+ pPair->value = copystring( NewValue );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will merge in the entities from the instance into
+// the main map.
+// Input : pInstanceEntity - the entity of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ int max_entity_id = 0;
+ char temp[ 2048 ];
+ char NameFixup[ 128 ];
+ entity_t *WorldspawnEnt = NULL;
+ GameData::TNameFixup FixupStyle;
+
+ char *pTargetName = ValueForKey( pInstanceEntity, "targetname" );
+ char *pName = ValueForKey( pInstanceEntity, "name" );
+ if ( pTargetName[ 0 ] )
+ {
+ sprintf( NameFixup, "%s", pTargetName );
+ }
+ else if ( pName[ 0 ] )
+ {
+ sprintf( NameFixup, "%s", pName );
+ }
+ else
+ {
+ sprintf( NameFixup, "InstanceAuto%d", m_InstanceCount );
+ }
+
+ for( int i = 0; i < num_entities; i++ )
+ {
+ char *pID = ValueForKey( &entities[ i ], "hammerid" );
+ if ( pID[ 0 ] )
+ {
+ int value = atoi( pID );
+ if ( value > max_entity_id )
+ {
+ max_entity_id = value;
+ }
+ }
+ }
+
+ FixupStyle = ( GameData::TNameFixup )( IntForKey( pInstanceEntity, "fixup_style" ) );
+
+ for( int i = 0; i < Instance->num_entities; i++ )
+ {
+ entities[ num_entities + i ] = Instance->entities[ i ];
+
+ entity_t *entity = &entities[ num_entities + i ];
+ entity->firstbrush += ( nummapbrushes - Instance->nummapbrushes );
+
+ char *pID = ValueForKey( entity, "hammerid" );
+ if ( pID[ 0 ] )
+ {
+ int value = atoi( pID );
+ value += max_entity_id;
+ sprintf( temp, "%d", value );
+
+ SetKeyValue( entity, "hammerid", temp );
+ }
+
+ char *pEntity = ValueForKey( entity, "classname" );
+ if ( strcmpi( pEntity, "worldspawn" ) == 0 )
+ {
+ WorldspawnEnt = entity;
+ }
+ else
+ {
+ Vector inOrigin = entity->origin;
+ VectorTransform( inOrigin, InstanceMatrix, entity->origin );
+
+ // search for variables coming from the func_instance to replace inside of the instance
+ // this is done before entity fixup, so fixup may occur on the replaced value. Not sure if this is a desired order of operation yet.
+ for ( epair_t *ep = entity->epairs; ep != NULL; ep = ep->next )
+ {
+ ReplaceInstancePair( ep, pInstanceEntity );
+ }
+
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( "Remapping class %s\n", pEntity );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ GDclass *EntClass = GD.BeginInstanceRemap( pEntity, NameFixup, InstanceOrigin, InstanceAngle );
+ if ( EntClass )
+ {
+ for( int i = 0; i < EntClass->GetVariableCount(); i++ )
+ {
+ GDinputvariable *EntVar = EntClass->GetVariableAt( i );
+ char *pValue = ValueForKey( entity, ( char * )EntVar->GetName() );
+ if ( GD.RemapKeyValue( EntVar->GetName(), pValue, temp, FixupStyle ) )
+ {
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( " %d. Remapped %s: from %s to %s\n", i, EntVar->GetName(), pValue, temp );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ SetKeyValue( entity, EntVar->GetName(), temp );
+ }
+ else
+ {
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( " %d. Ignored %s: %s\n", i, EntVar->GetName(), pValue );
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ }
+ }
+ }
+
+ if ( strcmpi( pEntity, "func_simpleladder" ) == 0 )
+ { // hate having to do this, but the key values are so screwed up
+ AddLadderKeys( entity );
+/* Vector vInNormal, vOutNormal;
+
+ vInNormal.x = FloatForKey( entity, "normal.x" );
+ vInNormal.y = FloatForKey( entity, "normal.y" );
+ vInNormal.z = FloatForKey( entity, "normal.z" );
+ VectorRotate( vInNormal, InstanceMatrix, vOutNormal );
+
+ Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.x );
+ SetKeyValue( entity, "normal.x", temp );
+
+ Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.y );
+ SetKeyValue( entity, "normal.y", temp );
+
+ Q_snprintf( temp, sizeof( temp ), "%f", vOutNormal.z );
+ SetKeyValue( entity, "normal.z", temp );*/
+ }
+ }
+
+#ifdef MERGE_INSTANCE_DEBUG_INFO
+ Msg( "Instance Entity %d remapped to %d\n", i, num_entities + i );
+ Msg( " FirstBrush: from %d to %d\n", Instance->entities[ i ].firstbrush, entity->firstbrush );
+ Msg( " KV Pairs:\n" );
+ for ( epair_t *ep = entity->epairs; ep->next != NULL; ep = ep->next )
+ {
+ Msg( " %s %s\n", ep->key, ep->value );
+ }
+#endif // #ifdef MERGE_INSTANCE_DEBUG_INFO
+ }
+
+ // search for variables coming from the func_instance to replace inside of the instance
+ // this is done before connection fix up, so fix up may occur on the replaced value. Not sure if this is a desired order of operation yet.
+ for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
+ {
+ ReplaceInstancePair( Connection->m_Pair, pInstanceEntity );
+ }
+
+ for( CConnectionPairs *Connection = Instance->m_ConnectionPairs; Connection; Connection = Connection->m_Next )
+ {
+ char *newValue, *oldValue;
+ char origValue[ 4096 ];
+ int extraLen = 0;
+
+ oldValue = Connection->m_Pair->value;
+ strcpy( origValue, oldValue );
+ char *pos = strchr( origValue, ',' );
+ if ( pos )
+ { // null terminate the first field
+ *pos = NULL;
+ extraLen = strlen( pos + 1) + 1; // for the comma we just null'd
+ }
+
+ if ( GD.RemapNameField( origValue, temp, FixupStyle ) )
+ {
+ newValue = new char [ strlen( temp ) + extraLen + 1 ];
+ strcpy( newValue, temp );
+ if ( pos )
+ {
+ strcat( newValue, "," );
+ strcat( newValue, pos + 1 );
+ }
+
+ Connection->m_Pair->value = newValue;
+ delete oldValue;
+ }
+ }
+
+ num_entities += Instance->num_entities;
+
+ MoveBrushesToWorldGeneral( WorldspawnEnt );
+ WorldspawnEnt->numbrushes = 0;
+ WorldspawnEnt->epairs = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will translate overlays from the instance into
+// the main map.
+// Input : InstanceEntityNum - the entity number of the func_instance
+// Instance - the map file of the instance
+// InstanceOrigin - the translation of the instance
+// InstanceAngle - the rotation of the instance
+// InstanceMatrix - the translation / rotation matrix of the instance
+// Output : none
+//-----------------------------------------------------------------------------
+void CMapFile::MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix )
+{
+ for( int i = Instance->m_StartMapOverlays; i < g_aMapOverlays.Count(); i++ )
+ {
+ Overlay_Translate( &g_aMapOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
+ }
+ for( int i = Instance->m_StartMapWaterOverlays; i < g_aMapWaterOverlays.Count(); i++ )
+ {
+ Overlay_Translate( &g_aMapWaterOverlays[ i ], InstanceOrigin, InstanceAngle, InstanceMatrix );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads a VMF or MAP file. If the file has a .MAP extension, the MAP
+// loader is used, otherwise the file is assumed to be in VMF format.
+// Input : pszFileName - Full path of the map file to load.
+//-----------------------------------------------------------------------------
+bool LoadMapFile( const char *pszFileName )
+{
+ bool bLoadingManifest = false;
+ CManifest *pMainManifest = NULL;
+ ChunkFileResult_t eResult;
+
+ //
+ // Dummy this up for the texture handling. This can be removed when old .MAP file
+ // support is removed.
+ //
+ g_nMapFileVersion = 400;
+
+ const char *pszExtension =V_GetFileExtension( pszFileName );
+ if ( pszExtension && strcmpi( pszExtension, "vmm" ) == 0 )
+ {
+ pMainManifest = new CManifest();
+ if ( pMainManifest->LoadVMFManifest( pszFileName ) )
+ {
+ eResult = ChunkFile_Ok;
+ pszFileName = pMainManifest->GetInstancePath();
+ }
+ else
+ {
+ eResult = ChunkFile_Fail;
+ }
+ bLoadingManifest = true;
+ }
+ else
+ {
+ //
+ // Open the file.
+ //
+ CChunkFile File;
+ eResult = File.Open(pszFileName, ChunkFile_Read);
+
+ //
+ // Read the file.
+ //
+ if (eResult == ChunkFile_Ok)
+ {
+ int index = g_Maps.AddToTail( new CMapFile() );
+ g_LoadingMap = g_Maps[ index ];
+ if ( g_MainMap == NULL )
+ {
+ g_MainMap = g_LoadingMap;
+ }
+
+ if ( g_MainMap == g_LoadingMap || verbose )
+ {
+ Msg( "Loading %s\n", pszFileName );
+ }
+
+
+ // reset the displacement info count
+ // nummapdispinfo = 0;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("world", (ChunkHandler_t)LoadEntityCallback, 0);
+ Handlers.AddHandler("entity", (ChunkHandler_t)LoadEntityCallback, 0);
+
+ File.PushHandlers(&Handlers);
+
+ //
+ // Read the sub-chunks. We ignore keys in the root of the file.
+ //
+ while (eResult == ChunkFile_Ok)
+ {
+ eResult = File.ReadChunk();
+ }
+
+ File.PopHandlers();
+ }
+ else
+ {
+ Error("Error opening %s: %s.\n", pszFileName, File.GetErrorText(eResult));
+ }
+ }
+
+ if ((eResult == ChunkFile_Ok) || (eResult == ChunkFile_EOF))
+ {
+ // Update the overlay/side list(s).
+ Overlay_UpdateSideLists( g_LoadingMap->m_StartMapOverlays );
+ OverlayTransition_UpdateSideLists( g_LoadingMap->m_StartMapWaterOverlays );
+
+ g_LoadingMap->CheckForInstances( pszFileName );
+
+ if ( pMainManifest )
+ {
+ pMainManifest->CordonWorld();
+ }
+
+ ClearBounds (g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
+ for (int i=0 ; i<g_MainMap->entities[0].numbrushes ; i++)
+ {
+ // HLTOOLS: Raise map limits
+ if (g_LoadingMap->mapbrushes[i].mins[0] > MAX_COORD_INTEGER)
+ {
+ continue; // no valid points
+ }
+
+ AddPointToBounds (g_LoadingMap->mapbrushes[i].mins, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
+ AddPointToBounds (g_LoadingMap->mapbrushes[i].maxs, g_LoadingMap->map_mins, g_LoadingMap->map_maxs);
+ }
+
+ qprintf ("%5i brushes\n", g_LoadingMap->nummapbrushes);
+ qprintf ("%5i clipbrushes\n", g_LoadingMap->c_clipbrushes);
+ qprintf ("%5i total sides\n", g_LoadingMap->nummapbrushsides);
+ qprintf ("%5i boxbevels\n", g_LoadingMap->c_boxbevels);
+ qprintf ("%5i edgebevels\n", g_LoadingMap->c_edgebevels);
+ qprintf ("%5i entities\n", g_LoadingMap->num_entities);
+ qprintf ("%5i planes\n", g_LoadingMap->nummapplanes);
+ qprintf ("%5i areaportals\n", g_LoadingMap->c_areaportals);
+ qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", g_LoadingMap->map_mins[0],g_LoadingMap->map_mins[1],g_LoadingMap->map_mins[2],
+ g_LoadingMap->map_maxs[0],g_LoadingMap->map_maxs[1],g_LoadingMap->map_maxs[2]);
+
+ //TestExpandBrushes();
+
+ // Clear the error reporting
+ g_MapError.ClearState();
+ }
+
+ if ( g_MainMap == g_LoadingMap )
+ {
+ num_entities = g_MainMap->num_entities;
+ memcpy( entities, g_MainMap->entities, sizeof( g_MainMap->entities ) );
+ }
+ g_LoadingMap->ForceFuncAreaPortalWindowContents();
+
+ return ( ( eResult == ChunkFile_Ok ) || ( eResult == ChunkFile_EOF ) );
+}
+
+ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
+{
+ return g_LoadingMap->LoadSideCallback( pFile, pSideInfo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pFile -
+// pParent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CMapFile::LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo)
+{
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ {
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ }
+
+ pSideInfo->pSide = &brushsides[nummapbrushsides];
+
+ side_t *side = pSideInfo->pSide;
+ mapbrush_t *b = pSideInfo->pBrush;
+ g_MapError.BrushSide( pSideInfo->nSideIndex++ );
+
+ // initialize the displacement info
+ pSideInfo->pSide->pMapDisp = NULL;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler( "dispinfo", ( ChunkHandler_t )LoadDispInfoCallback, &side->pMapDisp );
+
+ //
+ // Read the side chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSideKeyCallback, pSideInfo);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ side->contents |= pSideInfo->nBaseContents;
+ side->surf |= pSideInfo->nBaseFlags;
+ pSideInfo->td.flags |= pSideInfo->nBaseFlags;
+
+ if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+ {
+ side->contents |= CONTENTS_DETAIL;
+ }
+
+ if (fulldetail )
+ {
+ side->contents &= ~CONTENTS_DETAIL;
+ }
+
+ if (!(side->contents & (ALL_VISIBLE_CONTENTS | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) )
+ {
+ side->contents |= CONTENTS_SOLID;
+ }
+
+ // hints and skips are never detail, and have no content
+ if (side->surf & (SURF_HINT|SURF_SKIP) )
+ {
+ side->contents = 0;
+ }
+
+ //
+ // find the plane number
+ //
+ int planenum = PlaneFromPoints(pSideInfo->planepts[0], pSideInfo->planepts[1], pSideInfo->planepts[2]);
+ if (planenum != -1)
+ {
+ //
+ // See if the plane has been used already.
+ //
+ int k;
+ for ( k = 0; k < b->numsides; k++)
+ {
+ side_t *s2 = b->original_sides + k;
+ if (s2->planenum == planenum)
+ {
+ g_MapError.ReportWarning("duplicate plane");
+ break;
+ }
+ if ( s2->planenum == (planenum^1) )
+ {
+ g_MapError.ReportWarning("mirrored plane");
+ break;
+ }
+ }
+
+ //
+ // If the plane hasn't been used already, keep this side.
+ //
+ if (k == b->numsides)
+ {
+ side = b->original_sides + b->numsides;
+ side->planenum = planenum;
+ if ( !onlyents )
+ {
+ side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum], &pSideInfo->td, vec3_origin);
+ }
+
+ // save the td off in case there is an origin brush and we
+ // have to recalculate the texinfo
+ if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
+ g_MapError.ReportError ("MAX_MAP_BRUSHSIDES");
+ side_brushtextures[nummapbrushsides] = pSideInfo->td;
+ nummapbrushsides++;
+ b->numsides++;
+
+#ifdef VSVMFIO
+ // Tell Maya We Have Another Side
+ if ( CVmfImport::GetVmfImporter() )
+ {
+ CVmfImport::GetVmfImporter()->AddSideCallback(
+ b, side, pSideInfo->td,
+ pSideInfo->planepts[ 0 ], pSideInfo->planepts[ 1 ], pSideInfo->planepts[ 2 ] );
+ }
+#endif // VSVMFIO
+
+ }
+ }
+ else
+ {
+ g_MapError.ReportWarning("plane with no normal");
+ }
+ }
+
+ return(eResult);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : szKey -
+// szValue -
+// pSideInfo -
+// Output :
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadSideKeyCallback(const char *szKey, const char *szValue, LoadSide_t *pSideInfo)
+{
+ if (!stricmp(szKey, "plane"))
+ {
+ int nRead = sscanf(szValue, "(%f %f %f) (%f %f %f) (%f %f %f)",
+ &pSideInfo->planepts[0][0], &pSideInfo->planepts[0][1], &pSideInfo->planepts[0][2],
+ &pSideInfo->planepts[1][0], &pSideInfo->planepts[1][1], &pSideInfo->planepts[1][2],
+ &pSideInfo->planepts[2][0], &pSideInfo->planepts[2][1], &pSideInfo->planepts[2][2]);
+
+ if (nRead != 9)
+ {
+ g_MapError.ReportError("parsing plane definition");
+ }
+ }
+ else if (!stricmp(szKey, "material"))
+ {
+ // Get the material name.
+ if( g_ReplaceMaterials )
+ {
+ szValue = ReplaceMaterialName( szValue );
+ }
+
+ strcpy(pSideInfo->td.name, szValue);
+ g_MapError.TextureState(szValue);
+
+ // Find default flags and values for this material.
+ int mt = FindMiptex(pSideInfo->td.name);
+ pSideInfo->td.flags = textureref[mt].flags;
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = textureref[mt].lightmapWorldUnitsPerLuxel;
+
+ pSideInfo->pSide->contents = textureref[mt].contents;
+ pSideInfo->pSide->surf = pSideInfo->td.flags;
+ }
+ else if (!stricmp(szKey, "uaxis"))
+ {
+ int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.UAxis[0], &pSideInfo->td.UAxis[1], &pSideInfo->td.UAxis[2], &pSideInfo->td.shift[0], &pSideInfo->td.textureWorldUnitsPerTexel[0]);
+ if (nRead != 5)
+ {
+ g_MapError.ReportError("parsing U axis definition");
+ }
+ }
+ else if (!stricmp(szKey, "vaxis"))
+ {
+ int nRead = sscanf(szValue, "[%f %f %f %f] %f", &pSideInfo->td.VAxis[0], &pSideInfo->td.VAxis[1], &pSideInfo->td.VAxis[2], &pSideInfo->td.shift[1], &pSideInfo->td.textureWorldUnitsPerTexel[1]);
+ if (nRead != 5)
+ {
+ g_MapError.ReportError("parsing V axis definition");
+ }
+ }
+ else if (!stricmp(szKey, "lightmapscale"))
+ {
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = atoi(szValue);
+ if (pSideInfo->td.lightmapWorldUnitsPerLuxel == 0.0f)
+ {
+ g_MapError.ReportWarning("luxel size of 0");
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = g_defaultLuxelSize;
+ }
+ pSideInfo->td.lightmapWorldUnitsPerLuxel *= g_luxelScale;
+ if (pSideInfo->td.lightmapWorldUnitsPerLuxel < g_minLuxelScale)
+ {
+ pSideInfo->td.lightmapWorldUnitsPerLuxel = g_minLuxelScale;
+ }
+ }
+ else if (!stricmp(szKey, "contents"))
+ {
+ pSideInfo->pSide->contents |= atoi(szValue);
+ }
+ else if (!stricmp(szKey, "flags"))
+ {
+ pSideInfo->td.flags |= atoi(szValue);
+ pSideInfo->pSide->surf = pSideInfo->td.flags;
+ }
+ else if (!stricmp(szKey, "id"))
+ {
+ pSideInfo->pSide->id = atoi( szValue );
+ }
+ else if (!stricmp(szKey, "smoothing_groups"))
+ {
+ pSideInfo->pSide->smoothingGroups = atoi( szValue );
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reads the connections chunk of the entity.
+// Input : pFile - Chunk file to load from.
+// pLoadEntity - Structure to receive loaded entity information.
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadConnectionsCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
+{
+ return(pFile->ReadChunk((KeyHandler_t)LoadConnectionsKeyCallback, pLoadEntity));
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses a key/value pair from the entity connections chunk.
+// Input : szKey - Key indicating the name of the entity output.
+// szValue - Comma delimited fields in the following format:
+// <target>,<input>,<parameter>,<delay>,<times to fire>
+// pLoadEntity - Structure to receive loaded entity information.
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
+{
+ return g_LoadingMap->LoadConnectionsKeyCallback( szKey, szValue, pLoadEntity );
+}
+
+ChunkFileResult_t CMapFile::LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity)
+{
+ //
+ // Create new input and fill it out.
+ //
+ epair_t *pOutput = new epair_t;
+
+ pOutput->key = new char [strlen(szKey) + 1];
+ pOutput->value = new char [strlen(szValue) + 1];
+
+ strcpy(pOutput->key, szKey);
+ strcpy(pOutput->value, szValue);
+
+ m_ConnectionPairs = new CConnectionPairs( pOutput, m_ConnectionPairs );
+
+ //
+ // Append it to the end of epairs list.
+ //
+ pOutput->next = NULL;
+
+ if (!pLoadEntity->pEntity->epairs)
+ {
+ pLoadEntity->pEntity->epairs = pOutput;
+ }
+ else
+ {
+ epair_t *ep;
+ for ( ep = pLoadEntity->pEntity->epairs; ep->next != NULL; ep = ep->next )
+ {
+ }
+ ep->next = pOutput;
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
+{
+ return g_LoadingMap->LoadSolidCallback( pFile, pLoadEntity );
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pFile -
+// pParent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t CMapFile::LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity)
+{
+ if (nummapbrushes == MAX_MAP_BRUSHES)
+ {
+ g_MapError.ReportError ("nummapbrushes == MAX_MAP_BRUSHES");
+ }
+
+ mapbrush_t *b = &mapbrushes[nummapbrushes];
+ b->original_sides = &brushsides[nummapbrushsides];
+ b->entitynum = num_entities-1;
+ b->brushnum = nummapbrushes - pLoadEntity->pEntity->firstbrush;
+
+ LoadSide_t SideInfo;
+ SideInfo.pBrush = b;
+ SideInfo.nSideIndex = 0;
+ SideInfo.nBaseContents = pLoadEntity->nBaseContents;
+ SideInfo.nBaseFlags = pLoadEntity->nBaseFlags;
+
+ //
+ // Set up handlers for the subchunks that we are interested in.
+ //
+ CChunkHandlerMap Handlers;
+ Handlers.AddHandler("side", (ChunkHandler_t)::LoadSideCallback, &SideInfo);
+
+ //
+ // Read the solid chunk.
+ //
+ pFile->PushHandlers(&Handlers);
+ ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadSolidKeyCallback, b);
+ pFile->PopHandlers();
+
+ if (eResult == ChunkFile_Ok)
+ {
+ // get the content for the entire brush
+ b->contents = BrushContents (b);
+
+ // allow detail brushes to be removed
+ if (nodetail && (b->contents & CONTENTS_DETAIL) && !HasDispInfo( b ) )
+ {
+ b->numsides = 0;
+ return(ChunkFile_Ok);
+ }
+
+ // allow water brushes to be removed
+ if (nowater && (b->contents & MASK_WATER) )
+ {
+ b->numsides = 0;
+ return(ChunkFile_Ok);
+ }
+
+ // create windings for sides and bounds for brush
+ MakeBrushWindings (b);
+
+ //
+ // brushes that will not be visible at all will never be
+ // used as bsp splitters
+ //
+ // only do this on the world entity
+ //
+ if ( b->entitynum == 0 )
+ {
+ if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+ {
+ if ( g_ClipTexinfo < 0 )
+ {
+ g_ClipTexinfo = b->original_sides[0].texinfo;
+ }
+ c_clipbrushes++;
+ for (int i=0 ; i<b->numsides ; i++)
+ {
+ b->original_sides[i].texinfo = TEXINFO_NODE;
+ }
+ }
+ }
+
+ //
+ // origin brushes are removed, but they set
+ // the rotation origin for the rest of the brushes
+ // in the entity. After the entire entity is parsed,
+ // the planenums and texinfos will be adjusted for
+ // the origin brush
+ //
+ if (b->contents & CONTENTS_ORIGIN)
+ {
+ char string[32];
+ Vector origin;
+
+ if (num_entities == 1)
+ {
+ Error("Brush %i: origin brushes not allowed in world", b->id);
+ }
+
+ VectorAdd (b->mins, b->maxs, origin);
+ VectorScale (origin, 0.5, origin);
+
+ sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
+ SetKeyValue (&entities[b->entitynum], "origin", string);
+
+ VectorCopy (origin, entities[b->entitynum].origin);
+
+ // don't keep this brush
+ b->numsides = 0;
+
+ return(ChunkFile_Ok);
+ }
+
+#ifdef VSVMFIO
+ if ( CVmfImport::GetVmfImporter() )
+ {
+ CVmfImport::GetVmfImporter()->MapBrushToMayaCallback( b );
+ }
+#endif // VSVMFIO
+
+ //
+ // find a map brushes with displacement surfaces and remove them from the "world"
+ //
+ if( HasDispInfo( b ) )
+ {
+ // add the base face data to the displacement surface
+ DispGetFaceInfo( b );
+
+ // don't keep this brush
+ b->numsides = 0;
+
+ return( ChunkFile_Ok );
+ }
+
+ AddBrushBevels (b);
+
+ nummapbrushes++;
+ pLoadEntity->pEntity->numbrushes++;
+ }
+ else
+ {
+ return eResult;
+ }
+
+ return(ChunkFile_Ok);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pFile -
+// parent -
+// Output : ChunkFileResult_t
+//-----------------------------------------------------------------------------
+ChunkFileResult_t LoadSolidKeyCallback(const char *szKey, const char *szValue, mapbrush_t *pLoadBrush)
+{
+ if (!stricmp(szKey, "id"))
+ {
+ pLoadBrush->id = atoi(szValue);
+ g_MapError.BrushState(pLoadBrush->id);
+ }
+
+ return ChunkFile_Ok;
+}
+
+
+/*
+================
+TestExpandBrushes
+
+Expands all the brush planes and saves a new map out
+================
+*/
+void CMapFile::TestExpandBrushes (void)
+{
+ FILE *f;
+ side_t *s;
+ int i, j, bn;
+ winding_t *w;
+ char *name = "expanded.map";
+ mapbrush_t *brush;
+ vec_t dist;
+
+ Msg ("writing %s\n", name);
+ f = fopen (name, "wb");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+ fprintf( f, "\"mapversion\" \"220\"\n\"sounds\" \"1\"\n\"MaxRange\" \"4096\"\n\"mapversion\" \"220\"\n\"wad\" \"vert.wad;dev.wad;generic.wad;spire.wad;urb.wad;cit.wad;water.wad\"\n" );
+
+
+ for (bn=0 ; bn<nummapbrushes ; bn++)
+ {
+ brush = &mapbrushes[bn];
+ fprintf (f, "{\n");
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = brush->original_sides + i;
+ dist = mapplanes[s->planenum].dist;
+ for (j=0 ; j<3 ; j++)
+ dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
+
+ w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist);
+
+ fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+ fprintf (f, "%s [ 0 0 1 -512 ] [ 0 -1 0 -256 ] 0 1 1 \n",
+ TexDataStringTable_GetString( GetTexData( texinfo[s->texinfo].texdata )->nameStringTableID ) );
+
+ FreeWinding (w);
+ }
+ fprintf (f, "}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+ Error ("can't proceed after expanding brushes");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: load in the displacement info "chunk" from the .map file into the
+// vbsp map displacement info data structure
+// Output: return the pointer to the displacement map
+//-----------------------------------------------------------------------------
+mapdispinfo_t *ParseDispInfoChunk( void )
+{
+ int i, j;
+ int vertCount;
+ mapdispinfo_t *pMapDispInfo;
+
+ //
+ // check to see if we exceeded the maximum displacement info list size
+ //
+ if( nummapdispinfo > MAX_MAP_DISPINFO )
+ g_MapError.ReportError( "ParseDispInfoChunk: nummapdispinfo > MAX_MAP_DISPINFO");
+
+ // get a pointer to the next available displacement info slot
+ pMapDispInfo = &mapdispinfo[nummapdispinfo];
+ nummapdispinfo++;
+
+ //
+ // get the chunk opener - "{"
+ //
+ GetToken( false );
+ if( strcmp( token, "{" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - {" );
+
+ //
+ //
+ // get the displacement info attribs
+ //
+ //
+
+ // power
+ GetToken( true );
+ pMapDispInfo->power = atoi( token );
+
+ // u and v mapping axes
+ for( i = 0; i < 2; i++ )
+ {
+ GetToken( false );
+ if( strcmp( token, "[" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - [" );
+
+ for( j = 0; j < 3; j++ )
+ {
+ GetToken( false );
+
+ if( i == 0 )
+ {
+ pMapDispInfo->uAxis[j] = atof( token );
+ }
+ else
+ {
+ pMapDispInfo->vAxis[j] = atof( token );
+ }
+ }
+
+ GetToken( false );
+ if( strcmp( token, "]" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - ]" );
+ }
+
+ // max displacement value
+ if( g_nMapFileVersion < 350 )
+ {
+ GetToken( false );
+ pMapDispInfo->maxDispDist = atof( token );
+ }
+
+ // minimum tesselation value
+ GetToken( false );
+ pMapDispInfo->minTess = atoi( token );
+
+ // light smoothing angle
+ GetToken( false );
+ pMapDispInfo->smoothingAngle = atof( token );
+
+ //
+ // get the displacement info displacement normals
+ //
+ GetToken( true );
+ pMapDispInfo->vectorDisps[0][0] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[0][1] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[0][2] = atof( token );
+
+ vertCount = ( ( ( 1 << pMapDispInfo->power ) + 1 ) * ( ( 1 << pMapDispInfo->power ) + 1 ) );
+ for( i = 1; i < vertCount; i++ )
+ {
+ GetToken( false );
+ pMapDispInfo->vectorDisps[i][0] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[i][1] = atof( token );
+ GetToken( false );
+ pMapDispInfo->vectorDisps[i][2] = atof( token );
+ }
+
+ //
+ // get the displacement info displacement values
+ //
+ GetToken( true );
+ pMapDispInfo->dispDists[0] = atof( token );
+
+ for( i = 1; i < vertCount; i++ )
+ {
+ GetToken( false );
+ pMapDispInfo->dispDists[i] = atof( token );
+ }
+
+ //
+ // get the chunk closer - "}"
+ //
+ GetToken( true );
+ if( strcmp( token, "}" ) )
+ g_MapError.ReportError( "ParseDispInfoChunk: Illegal Chunk! - }" );
+
+ // return the index of the displacement info slot
+ return pMapDispInfo;
+}
+
+
diff --git a/mp/src/utils/vbsp/map.h b/mp/src/utils/vbsp/map.h new file mode 100644 index 00000000..44b1d934 --- /dev/null +++ b/mp/src/utils/vbsp/map.h @@ -0,0 +1,18 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MAP_H
+#define MAP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// All the brush sides referenced by info_no_dynamic_shadow entities.
+extern CUtlVector<int> g_NoDynamicShadowSides;
+
+
+#endif // MAP_H
diff --git a/mp/src/utils/vbsp/materialpatch.cpp b/mp/src/utils/vbsp/materialpatch.cpp new file mode 100644 index 00000000..e05b979d --- /dev/null +++ b/mp/src/utils/vbsp/materialpatch.cpp @@ -0,0 +1,440 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "vbsp.h"
+#include "UtlBuffer.h"
+#include "utlsymbol.h"
+#include "utlrbtree.h"
+#include "KeyValues.h"
+#include "bsplib.h"
+#include "materialpatch.h"
+#include "tier1/strtools.h"
+
+// case insensitive
+static CUtlSymbolTable s_SymbolTable( 0, 32, true );
+
+struct NameTranslationLookup_t
+{
+ CUtlSymbol m_OriginalFileName;
+ CUtlSymbol m_PatchFileName;
+};
+
+static bool NameTranslationLessFunc( NameTranslationLookup_t const& src1,
+ NameTranslationLookup_t const& src2 )
+{
+ return src1.m_PatchFileName < src2.m_PatchFileName;
+}
+
+CUtlRBTree<NameTranslationLookup_t, int> s_MapPatchedMatToOriginalMat( 0, 256, NameTranslationLessFunc );
+
+void AddNewTranslation( const char *pOriginalMaterialName, const char *pNewMaterialName )
+{
+ NameTranslationLookup_t newEntry;
+
+ newEntry.m_OriginalFileName = s_SymbolTable.AddString( pOriginalMaterialName );
+ newEntry.m_PatchFileName = s_SymbolTable.AddString( pNewMaterialName );
+
+ s_MapPatchedMatToOriginalMat.Insert( newEntry );
+}
+
+const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName )
+{
+ const char *pRetName = NULL;
+ int id;
+ NameTranslationLookup_t lookup;
+ lookup.m_PatchFileName = s_SymbolTable.AddString( pPatchMaterialName );
+ do
+ {
+ id = s_MapPatchedMatToOriginalMat.Find( lookup );
+ if( id >= 0 )
+ {
+ NameTranslationLookup_t &found = s_MapPatchedMatToOriginalMat[id];
+ lookup.m_PatchFileName = found.m_OriginalFileName;
+ pRetName = s_SymbolTable.String( found.m_OriginalFileName );
+ }
+ } while( id >= 0 );
+ if( !pRetName )
+ {
+ // This isn't a patched material, so just return the original name.
+ return pPatchMaterialName;
+ }
+ return pRetName;
+}
+
+
+void CreateMaterialPatchRecursive( KeyValues *pOriginalKeyValues, KeyValues *pPatchKeyValues, int nKeys, const MaterialPatchInfo_t *pInfo )
+{
+ int i;
+ for( i = 0; i < nKeys; i++ )
+ {
+ const char *pVal = pOriginalKeyValues->GetString( pInfo[i].m_pKey, NULL );
+ if( !pVal )
+ continue;
+ if( pInfo[i].m_pRequiredOriginalValue && Q_stricmp( pVal, pInfo[i].m_pRequiredOriginalValue ) != 0 )
+ continue;
+ pPatchKeyValues->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
+ }
+ KeyValues *pScan;
+ for( pScan = pOriginalKeyValues->GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() )
+ {
+ CreateMaterialPatchRecursive( pScan, pPatchKeyValues->FindKey( pScan->GetName(), true ), nKeys, pInfo );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A version which allows you to patch multiple key values
+//-----------------------------------------------------------------------------
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType )
+{
+ char pOldVMTFile[ 512 ];
+ char pNewVMTFile[ 512 ];
+
+ AddNewTranslation( pOriginalMaterialName, pNewMaterialName );
+
+ Q_snprintf( pOldVMTFile, 512, "materials/%s.vmt", pOriginalMaterialName );
+ Q_snprintf( pNewVMTFile, 512, "materials/%s.vmt", pNewMaterialName );
+
+// printf( "Creating material patch file %s which points at %s\n", newVMTFile, oldVMTFile );
+
+ KeyValues *kv = new KeyValues( "patch" );
+ if ( !kv )
+ {
+ Error( "Couldn't allocate KeyValues for %s!!!", pNewMaterialName );
+ }
+
+ kv->SetString( "include", pOldVMTFile );
+
+ const char *pSectionName = (nPatchType == PATCH_INSERT) ? "insert" : "replace";
+ KeyValues *section = kv->FindKey( pSectionName, true );
+
+ if( nPatchType == PATCH_REPLACE )
+ {
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pOriginalMaterialName ) );
+ KeyValues *origkv = new KeyValues( "blah" );
+
+ if ( !origkv->LoadFromFile( g_pFileSystem, name ) )
+ {
+ origkv->deleteThis();
+ Assert( 0 );
+ return;
+ }
+
+ CreateMaterialPatchRecursive( origkv, section, nKeys, pInfo );
+ origkv->deleteThis();
+ }
+ else
+ {
+ for ( int i = 0; i < nKeys; ++i )
+ {
+ section->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue );
+ }
+ }
+
+ // Write patched .vmt into a memory buffer
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ kv->RecursiveSaveToFile( buf, 0 );
+
+ // Add to pak file for this .bsp
+ AddBufferToPak( GetPakFile(), pNewVMTFile, (void*)buf.Base(), buf.TellPut(), true );
+
+ // Cleanup
+ kv->deleteThis();
+}
+
+
+//-----------------------------------------------------------------------------
+// Patches a single keyvalue in a material
+//-----------------------------------------------------------------------------
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType )
+{
+ MaterialPatchInfo_t info;
+ info.m_pKey = pNewKey;
+ info.m_pValue = pNewValue;
+ CreateMaterialPatch( pOriginalMaterialName, pNewMaterialName, 1, &info, nPatchType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key
+//-----------------------------------------------------------------------------
+static bool DoesMaterialHaveKey( KeyValues *pKeyValues, const char *pKeyName )
+{
+ const char *pVal;
+ pVal = pKeyValues->GetString( pKeyName, NULL );
+ if ( pVal != NULL )
+ return true;
+
+ for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ if ( DoesMaterialHaveKey( pSubKey, pKeyName) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key/value pair
+//-----------------------------------------------------------------------------
+static bool DoesMaterialHaveKeyValuePair( KeyValues *pKeyValues, const char *pKeyName, const char *pSearchValue )
+{
+ const char *pVal;
+ pVal = pKeyValues->GetString( pKeyName, NULL );
+ if ( pVal != NULL && ( Q_stricmp( pSearchValue, pVal ) == 0 ) )
+ return true;
+
+ for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ if ( DoesMaterialHaveKeyValuePair( pSubKey, pKeyName, pSearchValue ) )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key
+//-----------------------------------------------------------------------------
+bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName )
+{
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
+ KeyValues *kv = new KeyValues( "blah" );
+
+ if ( !kv->LoadFromFile( g_pFileSystem, name ) )
+ {
+ kv->deleteThis();
+ return NULL;
+ }
+
+ bool retVal = DoesMaterialHaveKey( kv, pKeyName );
+
+ kv->deleteThis();
+ return retVal;
+}
+
+//-----------------------------------------------------------------------------
+// Scan material + all subsections for key/value pair
+//-----------------------------------------------------------------------------
+bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue )
+{
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
+ KeyValues *kv = new KeyValues( "blah" );
+
+ if ( !kv->LoadFromFile( g_pFileSystem, name ) )
+ {
+ kv->deleteThis();
+ return NULL;
+ }
+
+ bool retVal = DoesMaterialHaveKeyValuePair( kv, pKeyName, pSearchValue );
+
+ kv->deleteThis();
+ return retVal;
+}
+
+//-----------------------------------------------------------------------------
+// Gets a material value from a material. Ignores all patches
+//-----------------------------------------------------------------------------
+bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
+{
+ char name[512];
+ Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) );
+ KeyValues *kv = new KeyValues( "blah" );
+
+ if ( !kv->LoadFromFile( g_pFileSystem, name ) )
+ {
+// Assert( 0 );
+ kv->deleteThis();
+ return NULL;
+ }
+
+ const char *pTmpValue = kv->GetString( pKey, NULL );
+ if( pTmpValue )
+ {
+ Q_strncpy( pValue, pTmpValue, len );
+ }
+
+ kv->deleteThis();
+ return ( pTmpValue != NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the original material associated with a patched material
+//-----------------------------------------------------------------------------
+MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain )
+{
+ MaterialSystemMaterial_t matID;
+ matID = FindMaterial( GetOriginalMaterialNameForPatchedMaterial( materialName ), pFound, bComplain );
+ return matID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Load keyvalues from the local pack file, or from a file
+//-----------------------------------------------------------------------------
+bool LoadKeyValuesFromPackOrFile( const char *pFileName, KeyValues *pKeyValues )
+{
+ CUtlBuffer buf;
+ if ( ReadFileFromPak( GetPakFile(), pFileName, true, buf ) )
+ {
+ return pKeyValues->LoadFromBuffer( pFileName, buf );
+ }
+
+ return pKeyValues->LoadFromFile( g_pFileSystem, pFileName );
+}
+
+
+//-----------------------------------------------------------------------------
+// VMT parser
+//-----------------------------------------------------------------------------
+static void InsertKeyValues( KeyValues &dst, KeyValues& src, bool bCheckForExistence )
+{
+ KeyValues *pSrcVar = src.GetFirstSubKey();
+ while( pSrcVar )
+ {
+ if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) )
+ {
+ switch( pSrcVar->GetDataType() )
+ {
+ case KeyValues::TYPE_STRING:
+ dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() );
+ break;
+ case KeyValues::TYPE_INT:
+ dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() );
+ break;
+ case KeyValues::TYPE_FLOAT:
+ dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() );
+ break;
+ case KeyValues::TYPE_PTR:
+ dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() );
+ break;
+ }
+ }
+ pSrcVar = pSrcVar->GetNextKey();
+ }
+}
+
+static void ExpandPatchFile( KeyValues &keyValues )
+{
+ int nCount = 0;
+ while( nCount < 10 && stricmp( keyValues.GetName(), "patch" ) == 0 )
+ {
+// WriteKeyValuesToFile( "patch.txt", keyValues );
+ const char *pIncludeFileName = keyValues.GetString( "include" );
+ if( !pIncludeFileName )
+ return;
+
+ KeyValues * includeKeyValues = new KeyValues( "vmt" );
+ int nBufLen = Q_strlen( pIncludeFileName ) + Q_strlen( "materials/.vmt" ) + 1;
+ char *pFileName = ( char * )stackalloc( nBufLen );
+ Q_strncpy( pFileName, pIncludeFileName, nBufLen );
+ bool bSuccess = LoadKeyValuesFromPackOrFile( pFileName, includeKeyValues );
+ if ( !bSuccess )
+ {
+ includeKeyValues->deleteThis();
+ return;
+ }
+
+ KeyValues *pInsertSection = keyValues.FindKey( "insert" );
+ if( pInsertSection )
+ {
+ InsertKeyValues( *includeKeyValues, *pInsertSection, false );
+ keyValues = *includeKeyValues;
+ }
+
+ KeyValues *pReplaceSection = keyValues.FindKey( "replace" );
+ if( pReplaceSection )
+ {
+ InsertKeyValues( *includeKeyValues, *pReplaceSection, true );
+ keyValues = *includeKeyValues;
+ }
+
+ // Could add other commands here, like "delete", "rename", etc.
+
+ includeKeyValues->deleteThis();
+ nCount++;
+ }
+
+ if( nCount >= 10 )
+ {
+ Warning( "Infinite recursion in patch file?\n" );
+ }
+}
+
+KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags )
+{
+ // Load the underlying file
+ KeyValues *kv = new KeyValues( "blah" );
+
+ char pFullMaterialName[512];
+ Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
+ if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
+ {
+ // Assert( 0 );
+ kv->deleteThis();
+ return NULL;
+ }
+
+ if( nFlags & LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH )
+ {
+ ExpandPatchFile( *kv );
+ }
+
+ return kv;
+}
+
+void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv )
+{
+ char pFullMaterialName[512];
+ Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
+
+ // Write patched .vmt into a memory buffer
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ kv->RecursiveSaveToFile( buf, 0 );
+
+ // Add to pak file for this .bsp
+ AddBufferToPak( GetPakFile(), pFullMaterialName, (void*)buf.Base(), buf.TellPut(), true );
+
+ // Cleanup
+ kv->deleteThis();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a keyvalue from a *patched* material
+//-----------------------------------------------------------------------------
+bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len )
+{
+ // Load the underlying file so that we can check if env_cubemap is in there.
+ KeyValues *kv = new KeyValues( "blah" );
+
+ char pFullMaterialName[512];
+ Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
+ if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) )
+ {
+// Assert( 0 );
+ kv->deleteThis();
+ return NULL;
+ }
+
+ ExpandPatchFile( *kv );
+
+ const char *pTmpValue = kv->GetString( pKey, NULL );
+ if( pTmpValue )
+ {
+ Q_strncpy( pValue, pTmpValue, len );
+ }
+
+ kv->deleteThis();
+ return ( pTmpValue != NULL );
+}
diff --git a/mp/src/utils/vbsp/materialpatch.h b/mp/src/utils/vbsp/materialpatch.h new file mode 100644 index 00000000..334fe013 --- /dev/null +++ b/mp/src/utils/vbsp/materialpatch.h @@ -0,0 +1,60 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MATERIALPATCH_H
+#define MATERIALPATCH_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utilmatlib.h"
+
+struct MaterialPatchInfo_t
+{
+ const char *m_pKey;
+ const char *m_pRequiredOriginalValue; // NULL if you don't require one.
+ const char *m_pValue;
+ MaterialPatchInfo_t()
+ {
+ memset( this, 0, sizeof( *this ) );
+ }
+};
+
+enum MaterialPatchType_t
+{
+ PATCH_INSERT = 0, // Add the key no matter what
+ PATCH_REPLACE, // Add the key only if it exists
+};
+
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType );
+
+// A version which allows you to use multiple key values
+void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName,
+ int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType );
+
+// This gets a keyvalue from the *unpatched* version of the passed-in material
+bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
+
+// Gets a keyvalue from a *patched* material
+bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len );
+
+const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName );
+
+MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain = true );
+
+bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue );
+bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName );
+
+enum LoadMaterialKeyValuesFlags_t
+{
+ LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH = 1,
+};
+
+KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags );
+void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv );
+
+#endif // MATERIALPATCH_H
diff --git a/mp/src/utils/vbsp/materialsub.cpp b/mp/src/utils/vbsp/materialsub.cpp new file mode 100644 index 00000000..d8eb40df --- /dev/null +++ b/mp/src/utils/vbsp/materialsub.cpp @@ -0,0 +1,90 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: This file loads a KeyValues file containing material name mappings.
+// When the bsp is compiled, all materials listed in the file will
+// be replaced by the second material in the pair.
+//
+//=============================================================================
+
+#include "vbsp.h"
+#include "materialsub.h"
+#include "KeyValues.h"
+#include "tier1/strtools.h"
+
+bool g_ReplaceMaterials = false;
+
+static KeyValues *kv = 0;
+static KeyValues *allMapKeys = 0;
+static KeyValues *curMapKeys = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads the KeyValues file for materials replacements
+//-----------------------------------------------------------------------------
+void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname )
+{
+ // Careful with static variables
+ if( kv )
+ {
+ kv->deleteThis();
+ kv = 0;
+ }
+ if( allMapKeys )
+ allMapKeys = 0;
+ if( curMapKeys )
+ curMapKeys = 0;
+
+ Msg( "Loading Replacement Keys\n" );
+
+ // Attach the path to the keyValues file
+ char path[1024];
+ Q_snprintf( path, sizeof( path ), "%scfg\\materialsub.cfg", gamedir );
+
+ // Load the keyvalues file
+ kv = new KeyValues( "MaterialReplacements" );
+
+ Msg( "File path: %s", path );
+ if( !kv->LoadFromFile( g_pFileSystem, path ) )
+ {
+ Msg( "Failed to load KeyValues file!\n" );
+ g_ReplaceMaterials = false;
+ kv->deleteThis();
+ kv = 0;
+ return;
+ }
+
+ // Load global replace keys
+ allMapKeys = kv->FindKey( "AllMaps", true );
+
+ // Load keys for the current map
+ curMapKeys = kv->FindKey( mapname );
+
+ allMapKeys->ChainKeyValue( curMapKeys );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deletes all keys
+//-----------------------------------------------------------------------------
+void DeleteMaterialReplacementKeys( void )
+{
+ if( kv )
+ {
+ kv->deleteThis();
+ kv = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Replace the passed-in material name with a replacement name, if one exists
+//-----------------------------------------------------------------------------
+const char* ReplaceMaterialName( const char *name )
+{
+ // Look for the material name in the global and map KeyValues
+ // If it's not there, just return the original name
+
+ // HACK: This stinks - KeyValues won't take a string with '/' in it.
+ // If they did, this could be a simple pointer swap.
+ char newName[1024];
+ Q_strncpy( newName, name, sizeof( newName ) );
+ Q_FixSlashes( newName );
+ return allMapKeys->GetString( newName, name );
+}
\ No newline at end of file diff --git a/mp/src/utils/vbsp/materialsub.h b/mp/src/utils/vbsp/materialsub.h new file mode 100644 index 00000000..c1de0698 --- /dev/null +++ b/mp/src/utils/vbsp/materialsub.h @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: This file loads a KeyValues file containing material name mappings.
+// When the bsp is compiled, all materials listed in the file will
+// be replaced by the second material in the pair.
+//
+//=============================================================================
+
+#ifndef MATERIALSUB_H
+#define MATERIALSUB_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+extern bool g_ReplaceMaterials;
+
+// Setup / Cleanup
+void LoadMaterialReplacementKeys( const char *gamedir, const char *mapname );
+void DeleteMaterialReplacementKeys( void );
+
+// Takes a material name and returns it's replacement, if there is one.
+// If there isn't a replacement, it returns the original.
+const char* ReplaceMaterialName( const char *name );
+
+#endif // MATERIALSUB_H
\ No newline at end of file diff --git a/mp/src/utils/vbsp/nodraw.cpp b/mp/src/utils/vbsp/nodraw.cpp new file mode 100644 index 00000000..4fa493ea --- /dev/null +++ b/mp/src/utils/vbsp/nodraw.cpp @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+
+Vector draw_mins, draw_maxs;
+
+void Draw_ClearWindow (void)
+{
+}
+
+//============================================================
+
+#define GLSERV_PORT 25001
+
+
+void GLS_BeginScene (void)
+{
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+}
+
+void GLS_EndScene (void)
+{
+}
diff --git a/mp/src/utils/vbsp/normals.cpp b/mp/src/utils/vbsp/normals.cpp new file mode 100644 index 00000000..1696342c --- /dev/null +++ b/mp/src/utils/vbsp/normals.cpp @@ -0,0 +1,50 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "bsplib.h"
+#include "vbsp.h"
+
+
+void SaveVertexNormals( void )
+{
+ int i, j;
+ dface_t *f;
+ texinfo_t *tex;
+
+
+ g_numvertnormalindices = 0;
+ g_numvertnormals = 0;
+
+ for( i = 0 ;i<numfaces ; i++ )
+ {
+ f = &dfaces[i];
+ tex = &texinfo[f->texinfo];
+
+ for( j = 0; j < f->numedges; j++ )
+ {
+ if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
+ {
+ Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES (%d)", MAX_MAP_VERTNORMALINDICES );
+ }
+
+ g_vertnormalindices[g_numvertnormalindices] = g_numvertnormals;
+ g_numvertnormalindices++;
+ }
+
+ // Add this face plane's normal.
+ // Note: this doesn't do an exhaustive vertex normal match because the vrad does it.
+ // The result is that a little extra memory is wasted coming out of vbsp, but it
+ // goes away after vrad.
+ if( g_numvertnormals == MAX_MAP_VERTNORMALS )
+ {
+ Error( "g_numvertnormals == MAX_MAP_VERTNORMALS (%d)", MAX_MAP_VERTNORMALS );
+ }
+
+ g_vertnormals[g_numvertnormals] = dplanes[f->planenum].normal;
+ g_numvertnormals++;
+ }
+}
diff --git a/mp/src/utils/vbsp/notes.txt b/mp/src/utils/vbsp/notes.txt new file mode 100644 index 00000000..b86b3e3f --- /dev/null +++ b/mp/src/utils/vbsp/notes.txt @@ -0,0 +1,66 @@ +// -----------------------------------------------------------------
+// JAY:
+//
+DONE: Fix tools for HL2/TF2 coordinate space
+DONE: Load textures from Half-Life .WADs
+DONE: Write out Q2 texture format (not miptex)
+DONE: Write test map viewer
+DONE: Test detail brushes
+DONE: view portals to test
+NOT DOING:Write out HL style collision trees
+DONE: new engine loader
+DONE: new vis in HL2 engine - looks simple now
+DONE: Do QRAD backwards? i.e. use Valve QRAD, and merge in the Q2 file formats? probably
+DONE: Integrate Ken's qrad code into qrad3
+DONE: add area portal visibility to HL2 engine
+DONE: write area portal entities for HL2/TF2
+DONE: test area portal code
+Split clusters for outdoor vis
+
+// -----------------------------------------------------------------
+
+QBSP3 Chop is based on recursive subdivision.
+ - Force natural alignment of some sort to eliminate slivers where brushes meet ?
+
+
+Use Q2 style ladder indicator? yes
+Use Q2 style friction indicator? probably or not if physics based
+
+
+// -----------------------------------------------------------------
+// CHARLIE:
+//
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+NOTE: set DISP_PROTO to compile with displacement map info -- not on by default until
+ the prototype is done, the checked in .exe does not have displacement map functionality
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+DONE: put DISP_PROTO defines around all of the displacement code until prototyping is done
+DONE: add displacement map structure
+DONE: add displacement face structure
+DONE: change face/side structures to accept displacement texinfo and displacement face indices
+DONE: change .map loader to parse displacment map info
+DONE: don't allow merge or subdivision of displacement faces
+DONE: when splitting brushes, the new generated side get initialized to -1
+DONE: add find displacement face functionality, then create it if not found
+DONE: add find displacement map functionality, then create it if not found
+DONE: initialize the displacement data before loading the .map file
+initialize the face data with dispface and dispmap = -1, is this necessary????
+DONE: copy from bsp tool face to bsp file face -- the displacement info
+DONE: add/copy lumps
+DONE: swap data for writing to bsp -- not really necessary, but to keep in sync with the rest
+DONE: write .bsp data out
+DONE: add displacement data to .bsp statistics -- print file
+
+Test maps:
+DONE: map where disp face gets split by block node
+DONE: map where disp face attempts merge/split
+DONE: map with texture disp face
+DONE: map with lots of disp faces
+DONE: map with multiple disp faces referencing one map
+DONE: map with multiple disp faces on one brush
+DONE: map with multiple disp faces on one brush referencing one map
+DONE: map with funky texture split encased on one portal
+
+//------------------------------------------------------------------
\ No newline at end of file diff --git a/mp/src/utils/vbsp/overlay.cpp b/mp/src/utils/vbsp/overlay.cpp new file mode 100644 index 00000000..fcd2e924 --- /dev/null +++ b/mp/src/utils/vbsp/overlay.cpp @@ -0,0 +1,487 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "disp_vbsp.h"
+#include "builddisp.h"
+#include "mathlib/vmatrix.h"
+
+void Overlay_BuildBasisOrigin( doverlay_t *pOverlay );
+
+// Overlay list.
+CUtlVector<mapoverlay_t> g_aMapOverlays;
+CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int Overlay_GetFromEntity( entity_t *pMapEnt )
+{
+ int iAccessorID = -1;
+
+ // Allocate the new overlay.
+ int iOverlay = g_aMapOverlays.AddToTail();
+ mapoverlay_t *pMapOverlay = &g_aMapOverlays[iOverlay];
+
+ // Get the overlay data.
+ pMapOverlay->nId = g_aMapOverlays.Count() - 1;
+
+ if ( ValueForKey( pMapEnt, "targetname" )[ 0 ] != '\0' )
+ {
+ // Overlay has a name, remember it's ID for accessing
+ iAccessorID = pMapOverlay->nId;
+ }
+
+ pMapOverlay->flU[0] = FloatForKey( pMapEnt, "StartU" );
+ pMapOverlay->flU[1] = FloatForKey( pMapEnt, "EndU" );
+ pMapOverlay->flV[0] = FloatForKey( pMapEnt, "StartV" );
+ pMapOverlay->flV[1] = FloatForKey( pMapEnt, "EndV" );
+
+ pMapOverlay->flFadeDistMinSq = FloatForKey( pMapEnt, "fademindist" );
+ if ( pMapOverlay->flFadeDistMinSq > 0 )
+ {
+ pMapOverlay->flFadeDistMinSq *= pMapOverlay->flFadeDistMinSq;
+ }
+
+ pMapOverlay->flFadeDistMaxSq = FloatForKey( pMapEnt, "fademaxdist" );
+ if ( pMapOverlay->flFadeDistMaxSq > 0 )
+ {
+ pMapOverlay->flFadeDistMaxSq *= pMapOverlay->flFadeDistMaxSq;
+ }
+
+ GetVectorForKey( pMapEnt, "BasisOrigin", pMapOverlay->vecOrigin );
+
+ pMapOverlay->m_nRenderOrder = IntForKey( pMapEnt, "RenderOrder" );
+ if ( pMapOverlay->m_nRenderOrder < 0 || pMapOverlay->m_nRenderOrder >= OVERLAY_NUM_RENDER_ORDERS )
+ Error( "Overlay (%s) at %f %f %f has invalid render order (%d).\n", ValueForKey( pMapEnt, "material" ),
+ pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z,
+ pMapOverlay->m_nRenderOrder );
+
+ GetVectorForKey( pMapEnt, "uv0", pMapOverlay->vecUVPoints[0] );
+ GetVectorForKey( pMapEnt, "uv1", pMapOverlay->vecUVPoints[1] );
+ GetVectorForKey( pMapEnt, "uv2", pMapOverlay->vecUVPoints[2] );
+ GetVectorForKey( pMapEnt, "uv3", pMapOverlay->vecUVPoints[3] );
+
+ GetVectorForKey( pMapEnt, "BasisU", pMapOverlay->vecBasis[0] );
+ GetVectorForKey( pMapEnt, "BasisV", pMapOverlay->vecBasis[1] );
+ GetVectorForKey( pMapEnt, "BasisNormal", pMapOverlay->vecBasis[2] );
+
+ const char *pMaterialName = ValueForKey( pMapEnt, "material" );
+ Assert( strlen( pMaterialName ) < OVERLAY_MAP_STRLEN );
+ if ( strlen( pMaterialName ) >= OVERLAY_MAP_STRLEN )
+ {
+ Error( "Overlay Material Name (%s) too long! > OVERLAY_MAP_STRLEN (%d)", pMaterialName, OVERLAY_MAP_STRLEN );
+ return -1;
+ }
+ strcpy( pMapOverlay->szMaterialName, pMaterialName );
+
+ // Convert the sidelist to side id(s).
+ const char *pSideList = ValueForKey( pMapEnt, "sides" );
+ char *pTmpList = ( char* )_alloca( strlen( pSideList ) + 1 );
+ strcpy( pTmpList, pSideList );
+ const char *pScan = strtok( pTmpList, " " );
+ if ( !pScan )
+ return iAccessorID;
+
+ pMapOverlay->aSideList.Purge();
+ pMapOverlay->aFaceList.Purge();
+
+ do
+ {
+ int nSideId;
+ if ( sscanf( pScan, "%d", &nSideId ) == 1 )
+ {
+ pMapOverlay->aSideList.AddToTail( nSideId );
+ }
+ } while ( ( pScan = strtok( NULL, " " ) ) );
+
+ return iAccessorID;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+side_t *GetSide( int nSideId )
+{
+ for( int iSide = 0; iSide < g_LoadingMap->nummapbrushsides; ++iSide )
+ {
+ if ( g_LoadingMap->brushsides[iSide].id == nSideId )
+ return &g_LoadingMap->brushsides[iSide];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_UpdateSideLists( int StartIndex )
+{
+ int nMapOverlayCount = g_aMapOverlays.Count();
+ for( int iMapOverlay = StartIndex; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
+ {
+ mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( iMapOverlay );
+ if ( pMapOverlay )
+ {
+ int nSideCount = pMapOverlay->aSideList.Count();
+ for( int iSide = 0; iSide < nSideCount; ++iSide )
+ {
+ side_t *pSide = GetSide( pMapOverlay->aSideList[iSide] );
+ if ( pSide )
+ {
+ if ( pSide->aOverlayIds.Find( pMapOverlay->nId ) == -1 )
+ {
+ pSide->aOverlayIds.AddToTail( pMapOverlay->nId );
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void OverlayTransition_UpdateSideLists( int StartIndex )
+{
+ int nOverlayCount = g_aMapWaterOverlays.Count();
+ for( int iOverlay = StartIndex; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ mapoverlay_t *pOverlay = &g_aMapWaterOverlays.Element( iOverlay );
+ if ( pOverlay )
+ {
+ int nSideCount = pOverlay->aSideList.Count();
+ for( int iSide = 0; iSide < nSideCount; ++iSide )
+ {
+ side_t *pSide = GetSide( pOverlay->aSideList[iSide] );
+ if ( pSide )
+ {
+ if ( pSide->aWaterOverlayIds.Find( pOverlay->nId ) == -1 )
+ {
+ pSide->aWaterOverlayIds.AddToTail( pOverlay->nId );
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_AddFaceToLists( int iFace, side_t *pSide )
+{
+ int nOverlayIdCount = pSide->aOverlayIds.Count();
+ for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
+ {
+ mapoverlay_t *pMapOverlay = &g_aMapOverlays.Element( pSide->aOverlayIds[iOverlayId] );
+ if ( pMapOverlay )
+ {
+ if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
+ {
+ pMapOverlay->aFaceList.AddToTail( iFace );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide )
+{
+ int nOverlayIdCount = pSide->aWaterOverlayIds.Count();
+ for( int iOverlayId = 0; iOverlayId < nOverlayIdCount; ++iOverlayId )
+ {
+ mapoverlay_t *pMapOverlay = &g_aMapWaterOverlays.Element( pSide->aWaterOverlayIds[iOverlayId] - ( MAX_MAP_OVERLAYS + 1 ) );
+ if ( pMapOverlay )
+ {
+ if( pMapOverlay->aFaceList.Find( iFace ) == -1 )
+ {
+ pMapOverlay->aFaceList.AddToTail( iFace );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_EmitOverlayFace( mapoverlay_t *pMapOverlay )
+{
+ Assert( g_nOverlayCount < MAX_MAP_OVERLAYS );
+ if ( g_nOverlayCount >= MAX_MAP_OVERLAYS )
+ {
+ Error ( "Too Many Overlays!\nMAX_MAP_OVERLAYS = %d", MAX_MAP_OVERLAYS );
+ return;
+ }
+
+ doverlay_t *pOverlay = &g_Overlays[g_nOverlayCount];
+ doverlayfade_t *pOverlayFade = &g_OverlayFades[g_nOverlayCount];
+
+ g_nOverlayCount++;
+
+ // Conver the map overlay into a .bsp overlay (doverlay_t).
+ if ( pOverlay )
+ {
+ pOverlay->nId = pMapOverlay->nId;
+
+ pOverlay->flU[0] = pMapOverlay->flU[0];
+ pOverlay->flU[1] = pMapOverlay->flU[1];
+ pOverlay->flV[0] = pMapOverlay->flV[0];
+ pOverlay->flV[1] = pMapOverlay->flV[1];
+
+ VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
+ VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
+ VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
+ VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
+
+ VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
+
+ VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
+
+ pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
+
+ // Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
+ pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
+ pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
+ pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
+
+ // Encode whether or not the v axis should be flipped.
+ Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
+ if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
+ {
+ pOverlay->vecUVPoints[3].z = 1.0f;
+ }
+
+ // Texinfo.
+ texinfo_t texInfo;
+ texInfo.flags = 0;
+ texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
+ for( int iVec = 0; iVec < 2; ++iVec )
+ {
+ for( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ }
+
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
+ }
+ pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
+
+ // Face List
+ int nFaceCount = pMapOverlay->aFaceList.Count();
+ Assert( nFaceCount < OVERLAY_BSP_FACE_COUNT );
+ if ( nFaceCount >= OVERLAY_BSP_FACE_COUNT )
+ {
+ Error( "Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
+ return;
+ }
+
+ pOverlay->SetFaceCount( nFaceCount );
+ for( int iFace = 0; iFace < nFaceCount; ++iFace )
+ {
+ pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
+ }
+ }
+
+ // Convert the map overlay fade data into a .bsp overlay fade (doverlayfade_t).
+ if ( pOverlayFade )
+ {
+ pOverlayFade->flFadeDistMinSq = pMapOverlay->flFadeDistMinSq;
+ pOverlayFade->flFadeDistMaxSq = pMapOverlay->flFadeDistMaxSq;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void OverlayTransition_EmitOverlayFace( mapoverlay_t *pMapOverlay )
+{
+ Assert( g_nWaterOverlayCount < MAX_MAP_WATEROVERLAYS );
+ if ( g_nWaterOverlayCount >= MAX_MAP_WATEROVERLAYS )
+ {
+ Error ( "Too many water overlays!\nMAX_MAP_WATEROVERLAYS = %d", MAX_MAP_WATEROVERLAYS );
+ return;
+ }
+
+ dwateroverlay_t *pOverlay = &g_WaterOverlays[g_nWaterOverlayCount];
+ g_nWaterOverlayCount++;
+
+ // Conver the map overlay into a .bsp overlay (doverlay_t).
+ if ( pOverlay )
+ {
+ pOverlay->nId = pMapOverlay->nId;
+
+ pOverlay->flU[0] = pMapOverlay->flU[0];
+ pOverlay->flU[1] = pMapOverlay->flU[1];
+ pOverlay->flV[0] = pMapOverlay->flV[0];
+ pOverlay->flV[1] = pMapOverlay->flV[1];
+
+ VectorCopy( pMapOverlay->vecUVPoints[0], pOverlay->vecUVPoints[0] );
+ VectorCopy( pMapOverlay->vecUVPoints[1], pOverlay->vecUVPoints[1] );
+ VectorCopy( pMapOverlay->vecUVPoints[2], pOverlay->vecUVPoints[2] );
+ VectorCopy( pMapOverlay->vecUVPoints[3], pOverlay->vecUVPoints[3] );
+
+ VectorCopy( pMapOverlay->vecOrigin, pOverlay->vecOrigin );
+
+ VectorCopy( pMapOverlay->vecBasis[2], pOverlay->vecBasisNormal );
+
+ pOverlay->SetRenderOrder( pMapOverlay->m_nRenderOrder );
+
+ // Encode the BasisU into the unused z component of the vecUVPoints 0, 1, 2
+ pOverlay->vecUVPoints[0].z = pMapOverlay->vecBasis[0].x;
+ pOverlay->vecUVPoints[1].z = pMapOverlay->vecBasis[0].y;
+ pOverlay->vecUVPoints[2].z = pMapOverlay->vecBasis[0].z;
+
+ // Encode whether or not the v axis should be flipped.
+ Vector vecCross = pMapOverlay->vecBasis[2].Cross( pMapOverlay->vecBasis[0] );
+ if ( vecCross.Dot( pMapOverlay->vecBasis[1] ) < 0.0f )
+ {
+ pOverlay->vecUVPoints[3].z = 1.0f;
+ }
+
+ // Texinfo.
+ texinfo_t texInfo;
+ texInfo.flags = 0;
+ texInfo.texdata = FindOrCreateTexData( pMapOverlay->szMaterialName );
+ for( int iVec = 0; iVec < 2; ++iVec )
+ {
+ for( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][iAxis] = 0.0f;
+ }
+
+ texInfo.lightmapVecsLuxelsPerWorldUnits[iVec][3] = -99999.0f;
+ texInfo.textureVecsTexelsPerWorldUnits[iVec][3] = -99999.0f;
+ }
+ pOverlay->nTexInfo = FindOrCreateTexInfo( texInfo );
+
+ // Face List
+ int nFaceCount = pMapOverlay->aFaceList.Count();
+ Assert( nFaceCount < WATEROVERLAY_BSP_FACE_COUNT );
+ if ( nFaceCount >= WATEROVERLAY_BSP_FACE_COUNT )
+ {
+ Error( "Water Overlay touching too many faces (touching %d, max %d)\nOverlay %s at %.1f %.1f %.1f", nFaceCount, OVERLAY_BSP_FACE_COUNT, pMapOverlay->szMaterialName, pMapOverlay->vecOrigin.x, pMapOverlay->vecOrigin.y, pMapOverlay->vecOrigin.z );
+ return;
+ }
+
+ pOverlay->SetFaceCount( nFaceCount );
+ for( int iFace = 0; iFace < nFaceCount; ++iFace )
+ {
+ pOverlay->aFaces[iFace] = pMapOverlay->aFaceList.Element( iFace );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Overlay_EmitOverlayFaces( void )
+{
+ int nMapOverlayCount = g_aMapOverlays.Count();
+ for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
+ {
+ Overlay_EmitOverlayFace( &g_aMapOverlays.Element( iMapOverlay ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void OverlayTransition_EmitOverlayFaces( void )
+{
+ int nMapOverlayCount = g_aMapWaterOverlays.Count();
+ for( int iMapOverlay = 0; iMapOverlay < nMapOverlayCount; ++iMapOverlay )
+ {
+ OverlayTransition_EmitOverlayFace( &g_aMapWaterOverlays.Element( iMapOverlay ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// These routines were mostly stolen from MapOverlay.cpp in Hammer
+//-----------------------------------------------------------------------------
+#define OVERLAY_BASIS_U 0
+#define OVERLAY_BASIS_V 1
+#define OVERLAY_BASIS_NORMAL 2
+#define OVERLAY_HANDLES_COUNT 4
+
+
+inline void TransformPoint( const VMatrix& matrix, Vector &point )
+{
+ Vector orgVector = point;
+ matrix.V3Mul( orgVector, point );
+}
+
+
+inline bool fequal( float value, float target, float delta) { return ( (value<(target+delta))&&(value>(target-delta)) ); }
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function translate / rotate an overlay.
+// Input : pOverlay - the overlay to be translated
+// OriginOffset - the translation
+// AngleOffset - the rotation
+// Matrix - the translation / rotation matrix
+// Output : none
+//-----------------------------------------------------------------------------
+void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix )
+{
+ VMatrix tmpMatrix( Matrix );
+
+ Vector temp = pOverlay->vecOrigin;
+ VectorTransform( temp, Matrix, pOverlay->vecOrigin );
+
+ // erase move component
+ tmpMatrix.SetTranslation( vec3_origin );
+
+ // check if matrix would still change something
+ if ( !tmpMatrix.IsIdentity() )
+ {
+ // make sure axes are normalized (they should be anyways)
+ pOverlay->vecBasis[OVERLAY_BASIS_U].NormalizeInPlace();
+ pOverlay->vecBasis[OVERLAY_BASIS_V].NormalizeInPlace();
+
+ Vector vecU = pOverlay->vecBasis[OVERLAY_BASIS_U];
+ Vector vecV = pOverlay->vecBasis[OVERLAY_BASIS_V];
+ Vector vecNormal = pOverlay->vecBasis[OVERLAY_BASIS_NORMAL];
+
+ TransformPoint( tmpMatrix, vecU );
+ TransformPoint( tmpMatrix, vecV );
+ TransformPoint( tmpMatrix, vecNormal );
+
+ float fScaleU = vecU.Length();
+ float fScaleV = vecV.Length();
+ float flScaleNormal = vecNormal.Length();
+
+ bool bIsUnit = ( fequal( fScaleU, 1.0f, 0.0001 ) && fequal( fScaleV, 1.0f, 0.0001 ) && fequal( flScaleNormal, 1.0f, 0.0001 ) );
+ bool bIsPerp = ( fequal( DotProduct( vecU, vecV ), 0.0f, 0.0025 ) && fequal( DotProduct( vecU, vecNormal ), 0.0f, 0.0025 ) && fequal( DotProduct( vecV, vecNormal ), 0.0f, 0.0025 ) );
+
+ // if ( fequal(fScaleU,1,0.0001) && fequal(fScaleV,1,0.0001) && fequal(DotProduct( vecU, vecV ),0,0.0025) )
+ if ( bIsUnit && bIsPerp )
+ {
+ // transformation doesnt scale or shear anything, so just update base axes
+ pOverlay->vecBasis[OVERLAY_BASIS_U] = vecU;
+ pOverlay->vecBasis[OVERLAY_BASIS_V] = vecV;
+ pOverlay->vecBasis[OVERLAY_BASIS_NORMAL] = vecNormal;
+ }
+ else
+ {
+ // more complex transformation, move UV coordinates, but leave base axes
+ for ( int iHandle=0; iHandle<OVERLAY_HANDLES_COUNT;iHandle++)
+ {
+ Vector vecUV = pOverlay->vecUVPoints[iHandle];
+ Vector vecPos = ( vecUV.x * pOverlay->vecBasis[OVERLAY_BASIS_U] + vecUV.y * pOverlay->vecBasis[OVERLAY_BASIS_V] );
+
+ // to transform in world space
+ TransformPoint( tmpMatrix, vecPos );
+
+ vecUV.x = pOverlay->vecBasis[OVERLAY_BASIS_U].Dot( vecPos );
+ vecUV.y = pOverlay->vecBasis[OVERLAY_BASIS_V].Dot( vecPos );
+
+ pOverlay->vecUVPoints[iHandle] = vecUV;
+ }
+ }
+ }
+
+}
diff --git a/mp/src/utils/vbsp/portals.cpp b/mp/src/utils/vbsp/portals.cpp new file mode 100644 index 00000000..ec2a2695 --- /dev/null +++ b/mp/src/utils/vbsp/portals.cpp @@ -0,0 +1,1684 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "utlvector.h"
+#include "mathlib/vmatrix.h"
+#include "iscratchpad3d.h"
+#include "csg.h"
+#include "fmtstr.h"
+
+int c_active_portals;
+int c_peak_portals;
+int c_boundary;
+int c_boundary_sides;
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal (void)
+{
+ static int s_PortalCount = 0;
+
+ portal_t *p;
+
+ if (numthreads == 1)
+ c_active_portals++;
+ if (c_active_portals > c_peak_portals)
+ c_peak_portals = c_active_portals;
+
+ p = (portal_t*)malloc (sizeof(portal_t));
+ memset (p, 0, sizeof(portal_t));
+ p->id = s_PortalCount;
+ ++s_PortalCount;
+
+ return p;
+}
+
+void FreePortal (portal_t *p)
+{
+ if (p->winding)
+ FreeWinding (p->winding);
+ if (numthreads == 1)
+ c_active_portals--;
+ free (p);
+}
+
+//==============================================================
+
+/*
+==============
+VisibleContents
+
+Returns the single content bit of the
+strongest visible content present
+==============
+*/
+int VisibleContents (int contents)
+{
+ int i;
+
+ for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
+ {
+ if (contents & i )
+ {
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+===============
+ClusterContents
+===============
+*/
+int ClusterContents (node_t *node)
+{
+ int c1, c2, c;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return node->contents;
+
+ c1 = ClusterContents(node->children[0]);
+ c2 = ClusterContents(node->children[1]);
+ c = c1|c2;
+
+ // a cluster may include some solid detail areas, but
+ // still be seen into
+ if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
+ c &= ~CONTENTS_SOLID;
+ return c;
+}
+
+/*
+=============
+Portal_VisFlood
+
+Returns true if the portal is empty or translucent, allowing
+the PVS calculation to see through it.
+The nodes on either side of the portal may actually be clusters,
+not leafs, so all contents should be ored together
+=============
+*/
+qboolean Portal_VisFlood (portal_t *p)
+{
+ int c1, c2;
+
+ if (!p->onnode)
+ return false; // to global outsideleaf
+
+ c1 = ClusterContents(p->nodes[0]);
+ c2 = ClusterContents(p->nodes[1]);
+
+ if (!VisibleContents (c1^c2))
+ return true;
+
+ if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+ c1 = 0;
+ if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
+ c2 = 0;
+
+ if ( (c1|c2) & CONTENTS_SOLID )
+ return false; // can't see through solid
+
+ if (! (c1 ^ c2))
+ return true; // identical on both sides
+
+ if (!VisibleContents (c1^c2))
+ return true;
+ return false;
+}
+
+
+/*
+===============
+Portal_EntityFlood
+
+The entity flood determines which areas are
+"outside" on the map, which are then filled in.
+Flowing from side s to side !s
+===============
+*/
+qboolean Portal_EntityFlood (portal_t *p, int s)
+{
+ if (p->nodes[0]->planenum != PLANENUM_LEAF
+ || p->nodes[1]->planenum != PLANENUM_LEAF)
+ Error ("Portal_EntityFlood: not a leaf");
+
+ // can never cross to a solid
+ if ( (p->nodes[0]->contents & CONTENTS_SOLID)
+ || (p->nodes[1]->contents & CONTENTS_SOLID) )
+ return false;
+
+ // can flood through everything else
+ return true;
+}
+
+qboolean Portal_AreaLeakFlood (portal_t *p, int s)
+{
+ if ( !Portal_EntityFlood( p, s ) )
+ return false;
+
+ // can never cross through areaportal
+ if ( (p->nodes[0]->contents & CONTENTS_AREAPORTAL)
+ || (p->nodes[1]->contents & CONTENTS_AREAPORTAL) )
+ return false;
+
+ // can flood through everything else
+ return true;
+}
+
+
+//=============================================================================
+
+int c_tinyportals;
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
+{
+ if (p->nodes[0] || p->nodes[1])
+ Error ("AddPortalToNode: allready included");
+
+ p->nodes[0] = front;
+ p->next[0] = front->portals;
+ front->portals = p;
+
+ p->nodes[1] = back;
+ p->next[1] = back->portals;
+ back->portals = p;
+}
+
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+void RemovePortalFromNode (portal_t *portal, node_t *l)
+{
+ portal_t **pp, *t;
+
+// remove reference to the current portal
+ pp = &l->portals;
+ while (1)
+ {
+ t = *pp;
+ if (!t)
+ Error ("RemovePortalFromNode: portal not in leaf");
+
+ if ( t == portal )
+ break;
+
+ if (t->nodes[0] == l)
+ pp = &t->next[0];
+ else if (t->nodes[1] == l)
+ pp = &t->next[1];
+ else
+ Error ("RemovePortalFromNode: portal not bounding leaf");
+ }
+
+ if (portal->nodes[0] == l)
+ {
+ *pp = portal->next[0];
+ portal->nodes[0] = NULL;
+ }
+ else if (portal->nodes[1] == l)
+ {
+ *pp = portal->next[1];
+ portal->nodes[1] = NULL;
+ }
+}
+
+//============================================================================
+
+void PrintPortal (portal_t *p)
+{
+ int i;
+ winding_t *w;
+
+ w = p->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ Msg ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
+ , w->p[i][1], w->p[i][2]);
+}
+
+// because of water areaportals support, the areaportal may not be the only brush on this node
+bspbrush_t *AreaportalBrushForNode( node_t *node )
+{
+ bspbrush_t *b = node->brushlist;
+ while ( b && !(b->original->contents & CONTENTS_AREAPORTAL) )
+ {
+ b = b->next;
+ }
+ Assert( b->original->entitynum != 0 );
+ return b;
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+// buffer space around sides of nodes
+#define SIDESPACE 8
+void MakeHeadnodePortals (tree_t *tree)
+{
+ Vector bounds[2];
+ int i, j, n;
+ portal_t *p, *portals[6];
+ plane_t bplanes[6], *pl;
+ node_t *node;
+
+ node = tree->headnode;
+
+// pad with some space so there will never be null volume leafs
+ for (i=0 ; i<3 ; i++)
+ {
+ bounds[0][i] = tree->mins[i] - SIDESPACE;
+ bounds[1][i] = tree->maxs[i] + SIDESPACE;
+ }
+
+ tree->outside_node.planenum = PLANENUM_LEAF;
+ tree->outside_node.brushlist = NULL;
+ tree->outside_node.portals = NULL;
+ tree->outside_node.contents = 0;
+
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<2 ; j++)
+ {
+ n = j*3 + i;
+
+ p = AllocPortal ();
+ portals[n] = p;
+
+ pl = &bplanes[n];
+ memset (pl, 0, sizeof(*pl));
+ if (j)
+ {
+ pl->normal[i] = -1;
+ pl->dist = -bounds[j][i];
+ }
+ else
+ {
+ pl->normal[i] = 1;
+ pl->dist = bounds[j][i];
+ }
+ p->plane = *pl;
+ p->winding = BaseWindingForPlane (pl->normal, pl->dist);
+ AddPortalToNodes (p, node, &tree->outside_node);
+ }
+
+// clip the basewindings by all the other planes
+ for (i=0 ; i<6 ; i++)
+ {
+ for (j=0 ; j<6 ; j++)
+ {
+ if (j == i)
+ continue;
+ ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
+ }
+ }
+}
+
+//===================================================
+
+
+/*
+================
+BaseWindingForNode
+================
+*/
+#define BASE_WINDING_EPSILON 0.001
+#define SPLIT_WINDING_EPSILON 0.001
+
+winding_t *BaseWindingForNode (node_t *node)
+{
+ winding_t *w;
+ node_t *n;
+ plane_t *plane;
+ Vector normal;
+ vec_t dist;
+
+ w = BaseWindingForPlane (g_MainMap->mapplanes[node->planenum].normal, g_MainMap->mapplanes[node->planenum].dist);
+
+ // clip by all the parents
+ for (n=node->parent ; n && w ; )
+ {
+ plane = &g_MainMap->mapplanes[n->planenum];
+
+ if (n->children[0] == node)
+ { // take front
+ ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
+ }
+ else
+ { // take back
+ VectorSubtract (vec3_origin, plane->normal, normal);
+ dist = -plane->dist;
+ ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
+ }
+ node = n;
+ n = n->parent;
+ }
+
+ return w;
+}
+
+//============================================================
+
+/*
+==================
+MakeNodePortal
+
+create the new portal by taking the full plane winding for the cutting plane
+and clipping it by all of parents of this node
+==================
+*/
+void MakeNodePortal (node_t *node)
+{
+ portal_t *new_portal, *p;
+ winding_t *w;
+ Vector normal;
+ float dist = 0.0f;
+ int side = 0;
+
+ w = BaseWindingForNode (node);
+
+ // clip the portal by all the other portals in the node
+ for (p = node->portals ; p && w; p = p->next[side])
+ {
+ if (p->nodes[0] == node)
+ {
+ side = 0;
+ VectorCopy (p->plane.normal, normal);
+ dist = p->plane.dist;
+ }
+ else if (p->nodes[1] == node)
+ {
+ side = 1;
+ VectorSubtract (vec3_origin, p->plane.normal, normal);
+ dist = -p->plane.dist;
+ }
+ else
+ {
+ Error ("CutNodePortals_r: mislinked portal");
+ }
+
+ ChopWindingInPlace (&w, normal, dist, 0.1);
+ }
+
+ if (!w)
+ {
+ return;
+ }
+
+ if (WindingIsTiny (w))
+ {
+ c_tinyportals++;
+ FreeWinding (w);
+ return;
+ }
+
+
+ new_portal = AllocPortal ();
+ new_portal->plane = g_MainMap->mapplanes[node->planenum];
+ new_portal->onnode = node;
+ new_portal->winding = w;
+
+ AddPortalToNodes (new_portal, node->children[0], node->children[1]);
+}
+
+
+/*
+==============
+SplitNodePortals
+
+Move or split the portals that bound node so that the node's
+children have portals instead of node.
+==============
+*/
+void SplitNodePortals (node_t *node)
+{
+ portal_t *p, *next_portal, *new_portal;
+ node_t *f, *b, *other_node;
+ int side = 0;
+ plane_t *plane;
+ winding_t *frontwinding, *backwinding;
+
+ plane = &g_MainMap->mapplanes[node->planenum];
+ f = node->children[0];
+ b = node->children[1];
+
+ for (p = node->portals ; p ; p = next_portal)
+ {
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ side = 1;
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+ next_portal = p->next[side];
+
+ other_node = p->nodes[!side];
+ RemovePortalFromNode (p, p->nodes[0]);
+ RemovePortalFromNode (p, p->nodes[1]);
+
+//
+// cut the portal into two portals, one on each side of the cut plane
+//
+ ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
+ SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
+
+ if (frontwinding && WindingIsTiny(frontwinding))
+ {
+ FreeWinding (frontwinding);
+ frontwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (backwinding && WindingIsTiny(backwinding))
+ {
+ FreeWinding (backwinding);
+ backwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (!frontwinding && !backwinding)
+ { // tiny windings on both sides
+ continue;
+ }
+
+ if (!frontwinding)
+ {
+ FreeWinding (backwinding);
+ if (side == 0)
+ AddPortalToNodes (p, b, other_node);
+ else
+ AddPortalToNodes (p, other_node, b);
+ continue;
+ }
+ if (!backwinding)
+ {
+ FreeWinding (frontwinding);
+ if (side == 0)
+ AddPortalToNodes (p, f, other_node);
+ else
+ AddPortalToNodes (p, other_node, f);
+ continue;
+ }
+
+ // the winding is split
+ new_portal = AllocPortal ();
+ *new_portal = *p;
+ new_portal->winding = backwinding;
+ FreeWinding (p->winding);
+ p->winding = frontwinding;
+
+ if (side == 0)
+ {
+ AddPortalToNodes (p, f, other_node);
+ AddPortalToNodes (new_portal, b, other_node);
+ }
+ else
+ {
+ AddPortalToNodes (p, other_node, f);
+ AddPortalToNodes (new_portal, other_node, b);
+ }
+ }
+
+ node->portals = NULL;
+}
+
+
+/*
+================
+CalcNodeBounds
+================
+*/
+void CalcNodeBounds (node_t *node)
+{
+ portal_t *p;
+ int s;
+ int i;
+
+ // calc mins/maxs for both leafs and nodes
+ ClearBounds (node->mins, node->maxs);
+ for (p = node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ for (i=0 ; i<p->winding->numpoints ; i++)
+ AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
+ }
+}
+
+
+/*
+==================
+MakeTreePortals_r
+==================
+*/
+void MakeTreePortals_r (node_t *node)
+{
+ int i;
+
+ CalcNodeBounds (node);
+ if (node->mins[0] >= node->maxs[0])
+ {
+ Warning("WARNING: node without a volume\n");
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (node->mins[i] < (MIN_COORD_INTEGER-SIDESPACE) || node->maxs[i] > (MAX_COORD_INTEGER+SIDESPACE))
+ {
+ const char *pMatName = "<NO BRUSH>";
+ // split by brush side
+ if ( node->side )
+ {
+ texinfo_t *pTexInfo = &texinfo[node->side->texinfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ }
+ Vector point = node->portals->winding->p[0];
+ Warning("WARNING: BSP node with unbounded volume (material: %s, near %s)\n", pMatName, VecToString(point) );
+ break;
+ }
+ }
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ MakeTreePortals_r (node->children[0]);
+ MakeTreePortals_r (node->children[1]);
+}
+
+/*
+==================
+MakeTreePortals
+==================
+*/
+void MakeTreePortals (tree_t *tree)
+{
+ MakeHeadnodePortals (tree);
+ MakeTreePortals_r (tree->headnode);
+}
+
+/*
+=========================================================
+
+FLOOD ENTITIES
+
+=========================================================
+*/
+
+//-----------------------------------------------------------------------------
+// Purpose: Floods outward from the given node, marking visited nodes with
+// the number of hops from a node with an entity. If we ever mark
+// the outside_node for this tree, we've leaked.
+// Input : node -
+// dist -
+//-----------------------------------------------------------------------------
+void FloodPortals_r (node_t *node, int dist)
+{
+ portal_t *p;
+ int s;
+
+ node->occupied = dist;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ // Skip nodes that have already been marked.
+ if (p->nodes[!s]->occupied)
+ continue;
+
+ // Skip portals that lead to or from nodes with solid contents.
+ if (!Portal_EntityFlood (p, s))
+ continue;
+
+ FloodPortals_r (p->nodes[!s], dist+1);
+ }
+}
+
+void FloodAreaLeak_r( node_t *node, int dist )
+{
+ portal_t *p;
+ int s;
+
+ node->occupied = dist;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ if (p->nodes[!s]->occupied)
+ continue;
+
+ if (!Portal_AreaLeakFlood (p, s))
+ continue;
+
+ FloodAreaLeak_r( p->nodes[!s], dist+1 );
+ }
+}
+
+void ClearOccupied_r( node_t *headnode )
+{
+ if ( !headnode )
+ return;
+
+ headnode->occupied = 0;
+ ClearOccupied_r( headnode->children[0] );
+ ClearOccupied_r( headnode->children[1] );
+}
+
+void FloodAreaLeak( node_t *headnode, node_t *pFirstSide )
+{
+ ClearOccupied_r( headnode );
+ FloodAreaLeak_r( pFirstSide, 2 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For the given entity at the given origin, finds the leaf node in the
+// BSP tree that the entity occupies.
+//
+// We then flood outward from that leaf to see if the entity leaks.
+// Input : headnode -
+// origin -
+// occupant -
+// Output : Returns false if the entity is in solid, true if it is not.
+//-----------------------------------------------------------------------------
+qboolean PlaceOccupant (node_t *headnode, Vector& origin, entity_t *occupant)
+{
+ node_t *node;
+ vec_t d;
+ plane_t *plane;
+
+ // find the leaf to start in
+ node = headnode;
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &g_MainMap->mapplanes[node->planenum];
+ d = DotProduct (origin, plane->normal) - plane->dist;
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ if (node->contents == CONTENTS_SOLID)
+ return false;
+
+ node->occupant = occupant;
+
+ // Flood outward from here to see if this entity leaks.
+ FloodPortals_r (node, 1);
+
+ return true;
+}
+
+/*
+=============
+FloodEntities
+
+Marks all nodes that can be reached by entites
+=============
+*/
+qboolean FloodEntities (tree_t *tree)
+{
+ int i;
+ Vector origin;
+ char *cl;
+ qboolean inside;
+ node_t *headnode;
+
+ headnode = tree->headnode;
+ qprintf ("--- FloodEntities ---\n");
+ inside = false;
+ tree->outside_node.occupied = 0;
+
+ for (i=1 ; i<num_entities ; i++)
+ {
+ GetVectorForKey (&entities[i], "origin", origin);
+ if (VectorCompare(origin, vec3_origin))
+ continue;
+
+ cl = ValueForKey (&entities[i], "classname");
+
+ origin[2] += 1; // so objects on floor are ok
+
+ // nudge playerstart around if needed so clipping hulls allways
+ // have a valid point
+ if (!strcmp (cl, "info_player_start"))
+ {
+ int x, y;
+
+ for (x=-16 ; x<=16 ; x += 16)
+ {
+ for (y=-16 ; y<=16 ; y += 16)
+ {
+ origin[0] += x;
+ origin[1] += y;
+ if (PlaceOccupant (headnode, origin, &entities[i]))
+ {
+ inside = true;
+ goto gotit;
+ }
+ origin[0] -= x;
+ origin[1] -= y;
+ }
+ }
+gotit: ;
+ }
+ else
+ {
+ if (PlaceOccupant (headnode, origin, &entities[i]))
+ inside = true;
+ }
+ }
+
+ if (!inside)
+ {
+ qprintf ("no entities in open -- no filling\n");
+ }
+
+ if (tree->outside_node.occupied)
+ {
+ qprintf ("entity reached from outside -- no filling\n" );
+ }
+
+ return (qboolean)(inside && !tree->outside_node.occupied);
+}
+
+
+/*
+=========================================================
+
+FLOOD AREAS
+
+=========================================================
+*/
+
+int c_areas;
+
+bool IsAreaportalNode( node_t *node )
+{
+ return ( node->contents & CONTENTS_AREAPORTAL ) ? true : false;
+}
+/*
+=============
+FloodAreas_r
+=============
+*/
+
+void FloodAreas_r (node_t *node, portal_t *pSeeThrough)
+{
+ portal_t *p;
+ int s;
+ bspbrush_t *b;
+ entity_t *e;
+
+ if ( IsAreaportalNode(node) )
+ {
+ // this node is part of an area portal
+ b = AreaportalBrushForNode( node );
+ e = &entities[b->original->entitynum];
+
+ // if the current area has allready touched this
+ // portal, we are done
+ if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
+ return;
+
+ // note the current area as bounding the portal
+ if (e->portalareas[1])
+ {
+ Warning("WARNING: areaportal entity %i (brush %i) touches > 2 areas\n", b->original->entitynum, b->original->id );
+ return;
+ }
+
+ if (e->portalareas[0])
+ {
+ e->portalareas[1] = c_areas;
+ e->m_pPortalsLeadingIntoAreas[1] = pSeeThrough;
+ }
+ else
+ {
+ e->portalareas[0] = c_areas;
+ e->m_pPortalsLeadingIntoAreas[0] = pSeeThrough;
+ }
+
+ return;
+ }
+
+ if (node->area)
+ return; // allready got it
+ node->area = c_areas;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+#if 0
+ if (p->nodes[!s]->occupied)
+ continue;
+#endif
+ if (!Portal_EntityFlood (p, s))
+ continue;
+
+ FloodAreas_r (p->nodes[!s], p);
+ }
+}
+
+/*
+=============
+FindAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void FindAreas_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FindAreas_r (node->children[0]);
+ FindAreas_r (node->children[1]);
+ return;
+ }
+
+ if (node->area)
+ return; // allready got it
+
+ if (node->contents & CONTENTS_SOLID)
+ return;
+
+ if (!node->occupied)
+ return; // not reachable by entities
+
+ // area portals are allways only flooded into, never
+ // out of
+ if (IsAreaportalNode(node))
+ return;
+
+ c_areas++;
+ FloodAreas_r (node, NULL);
+}
+
+
+void ReportAreaportalLeak( tree_t *tree, node_t *node )
+{
+ portal_t *p, *pStart = NULL;
+ int s;
+
+ // Find a portal out of this areaportal into empty space
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ if ( !Portal_EntityFlood( p, !s ) )
+ continue;
+ if ( p->nodes[!s]->contents & CONTENTS_AREAPORTAL )
+ continue;
+
+ pStart = p;
+ break;
+ }
+
+ if ( pStart )
+ {
+ s = pStart->nodes[0] == node;
+ Assert(!(pStart->nodes[s]->contents & CONTENTS_AREAPORTAL) );
+ // flood fill the area outside this areaportal brush
+ FloodAreaLeak( tree->headnode, pStart->nodes[s] );
+
+ // find the portal into the longest path around the portal
+ portal_t *pBest = NULL;
+ int bestDist = 0;
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ if ( p == pStart )
+ continue;
+
+ s = (p->nodes[1] == node);
+ if ( p->nodes[!s]->occupied > bestDist )
+ {
+ pBest = p;
+ bestDist = p->nodes[!s]->occupied;
+ }
+ }
+ if ( pBest )
+ {
+ s = (pBest->nodes[0] == node);
+ // write the linefile that goes from pBest to pStart
+ AreaportalLeakFile( tree, pStart, pBest, pBest->nodes[s] );
+ }
+ }
+}
+
+
+/*
+=============
+SetAreaPortalAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void SetAreaPortalAreas_r (tree_t *tree, node_t *node)
+{
+ bspbrush_t *b;
+ entity_t *e;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ SetAreaPortalAreas_r (tree, node->children[0]);
+ SetAreaPortalAreas_r (tree, node->children[1]);
+ return;
+ }
+
+ if (IsAreaportalNode(node))
+ {
+ if (node->area)
+ return; // already set
+
+ b = AreaportalBrushForNode( node );
+ e = &entities[b->original->entitynum];
+ node->area = e->portalareas[0];
+ if (!e->portalareas[1])
+ {
+ ReportAreaportalLeak( tree, node );
+ Warning("\nBrush %i: areaportal brush doesn't touch two areas\n", b->original->id);
+ return;
+ }
+ }
+}
+
+
+// Return a positive value between 0 and 2*PI telling the angle distance
+// from flBaseAngle to flTestAngle.
+float AngleOffset( float flBaseAngle, float flTestAngle )
+{
+ while( flTestAngle > flBaseAngle )
+ flTestAngle -= 2 * M_PI;
+
+ return fmod( flBaseAngle - flTestAngle, (float) (2 * M_PI) );
+}
+
+
+int FindUniquePoints( const Vector2D *pPoints, int nPoints, int *indexMap, int nMaxIndexMapPoints, float flTolerance )
+{
+ float flToleranceSqr = flTolerance * flTolerance;
+
+ // This could be slightly more efficient.
+ int nUniquePoints = 0;
+ for ( int i=0; i < nPoints; i++ )
+ {
+ int j;
+ for ( j=0; j < nUniquePoints; j++ )
+ {
+ if ( pPoints[i].DistToSqr( pPoints[indexMap[j]] ) < flToleranceSqr )
+ break;
+ }
+ if ( j == nUniquePoints )
+ {
+ if ( nUniquePoints >= nMaxIndexMapPoints )
+ Error( "FindUniquePoints: overflowed unique point list (size %d).", nMaxIndexMapPoints );
+
+ indexMap[nUniquePoints++] = i;
+ }
+ }
+
+ return nUniquePoints;
+}
+
+// Build a 2D convex hull of the set of points.
+// This essentially giftwraps the points as it walks around the perimeter.
+int Convex2D( Vector2D const *pPoints, int nPoints, int *indices, int nMaxIndices )
+{
+ int nIndices = 0;
+ bool touched[512];
+ int indexMap[512];
+
+ if( nPoints == 0 )
+ return 0;
+
+
+ // If we don't collapse the points into a unique set, we can loop around forever
+ // and max out nMaxIndices.
+ nPoints = FindUniquePoints( pPoints, nPoints, indexMap, ARRAYSIZE( indexMap ), 0.1f );
+ memset( touched, 0, nPoints*sizeof(touched[0]) );
+
+ // Find the (lower) left side.
+ int i;
+ int iBest = 0;
+ for( i=1; i < nPoints; i++ )
+ {
+ if( pPoints[indexMap[i]].x < pPoints[indexMap[iBest]].x ||
+ (pPoints[indexMap[i]].x == pPoints[indexMap[iBest]].x && pPoints[indexMap[i]].y < pPoints[indexMap[iBest]].y) )
+ {
+ iBest = i;
+ }
+ }
+
+ touched[iBest] = true;
+ indices[0] = indexMap[iBest];
+ nIndices = 1;
+
+ Vector2D curEdge( 0, 1 );
+
+ // Wind around clockwise.
+ while( 1 )
+ {
+ Vector2D const *pStartPoint = &pPoints[ indices[nIndices-1] ];
+
+ float flEdgeAngle = atan2( curEdge.y, curEdge.x );
+
+ int iMinAngle = -1;
+ float flMinAngle = 5000;
+
+ for( i=0; i < nPoints; i++ )
+ {
+ Vector2D vTo = pPoints[indexMap[i]] - *pStartPoint;
+ float flDistToSqr = vTo.LengthSqr();
+ if ( flDistToSqr <= 0.1f )
+ continue;
+
+ // Get the angle from the edge to this point.
+ float flAngle = atan2( vTo.y, vTo.x );
+ flAngle = AngleOffset( flEdgeAngle, flAngle );
+
+ if( fabs( flAngle - flMinAngle ) < 0.00001f )
+ {
+ float flDistToTestSqr = pStartPoint->DistToSqr( pPoints[iMinAngle] );
+
+ // If the angle is the same, pick the point farthest away.
+ // unless the current one is closing the face loop
+ if ( iMinAngle != indices[0] && flDistToSqr > flDistToTestSqr )
+ {
+ flMinAngle = flAngle;
+ iMinAngle = indexMap[i];
+ }
+ }
+ else if( flAngle < flMinAngle )
+ {
+ flMinAngle = flAngle;
+ iMinAngle = indexMap[i];
+ }
+ }
+
+ if( iMinAngle == -1 )
+ {
+ // Couldn't find a point?
+ Assert( false );
+ break;
+ }
+ else if( iMinAngle == indices[0] )
+ {
+ // Finished.
+ break;
+ }
+ else
+ {
+ // Add this point.
+ if( nIndices >= nMaxIndices )
+ break;
+
+ for ( int jj = 0; jj < nIndices; jj++ )
+ {
+ // if this assert hits, this routine is broken and is generating a spiral
+ // rather than a closed polygon - basically an edge overlap of some kind
+ Assert(indices[jj] != iMinAngle );
+ }
+
+ indices[nIndices] = iMinAngle;
+ ++nIndices;
+ }
+
+ curEdge = pPoints[indices[nIndices-1]] - pPoints[indices[nIndices-2]];
+ }
+
+ return nIndices;
+}
+
+void FindPortalsLeadingToArea_R(
+ node_t *pHeadNode,
+ int iSrcArea,
+ int iDestArea,
+ plane_t *pPlane,
+ CUtlVector<portal_t*> &portals )
+{
+ if (pHeadNode->planenum != PLANENUM_LEAF)
+ {
+ FindPortalsLeadingToArea_R( pHeadNode->children[0], iSrcArea, iDestArea, pPlane, portals );
+ FindPortalsLeadingToArea_R( pHeadNode->children[1], iSrcArea, iDestArea, pPlane, portals );
+ return;
+ }
+
+ // Ok.. this is a leaf, check its portals.
+ int s;
+ for( portal_t *p = pHeadNode->portals; p ;p = p->next[!s] )
+ {
+ s = (p->nodes[0] == pHeadNode);
+
+ if( !p->nodes[0]->occupied || !p->nodes[1]->occupied )
+ continue;
+
+ if( p->nodes[1]->area == iDestArea && p->nodes[0]->area == iSrcArea ||
+ p->nodes[0]->area == iDestArea && p->nodes[1]->area == iSrcArea )
+ {
+ // Make sure the plane normals point the same way.
+ plane_t *pMapPlane = &g_MainMap->mapplanes[p->onnode->planenum];
+ float flDot = fabs( pMapPlane->normal.Dot( pPlane->normal ) );
+ if( fabs( 1 - flDot ) < 0.01f )
+ {
+ Vector vPlanePt1 = pPlane->normal * pPlane->dist;
+ Vector vPlanePt2 = pMapPlane->normal * pMapPlane->dist;
+
+ if( vPlanePt1.DistToSqr( vPlanePt2 ) < 0.01f )
+ {
+ portals.AddToTail( p );
+ }
+ }
+ }
+ }
+}
+
+
+void EmitClipPortalGeometry( node_t *pHeadNode, portal_t *pPortal, int iSrcArea, dareaportal_t *dp )
+{
+ // Build a list of all the points in portals from the same original face.
+ CUtlVector<portal_t*> portals;
+ FindPortalsLeadingToArea_R(
+ pHeadNode,
+ iSrcArea,
+ dp->otherarea,
+ &pPortal->plane,
+ portals );
+
+ CUtlVector<Vector> points;
+ for( int iPortal=0; iPortal < portals.Size(); iPortal++ )
+ {
+ portal_t *pPointPortal = portals[iPortal];
+ winding_t *pWinding = pPointPortal->winding;
+ for( int i=0; i < pWinding->numpoints; i++ )
+ {
+ points.AddToTail( pWinding->p[i] );
+ }
+ }
+
+ // Get the 2D convex hull.
+
+ //// First transform them into a plane.
+ QAngle vAngles;
+ Vector vecs[3];
+
+ VectorAngles( pPortal->plane.normal, vAngles );
+ AngleVectors( vAngles, &vecs[0], &vecs[1], &vecs[2] );
+ VMatrix mTransform;
+ mTransform.Identity();
+ mTransform.SetBasisVectors( vecs[0], vecs[1], vecs[2] );
+ VMatrix mInvTransform = mTransform.Transpose();
+
+ int i;
+ CUtlVector<Vector2D> points2D;
+ for( i=0; i < points.Size(); i++ )
+ {
+ Vector vTest = mTransform * points[i];
+ points2D.AddToTail( Vector2D( vTest.y, vTest.z ) );
+ }
+
+ // Build the hull.
+ int indices[512];
+ int nIndices = Convex2D( points2D.Base(), points2D.Size(), indices, 512 );
+
+ // Output the hull.
+ dp->m_FirstClipPortalVert = g_nClipPortalVerts;
+ dp->m_nClipPortalVerts = nIndices;
+
+ if ( nIndices >= 32 )
+ {
+ Warning( "Warning: area portal has %d verts. Could be a vbsp bug.\n", nIndices );
+ }
+
+ if( dp->m_FirstClipPortalVert + dp->m_nClipPortalVerts >= MAX_MAP_PORTALVERTS )
+ {
+ Vector *p = pPortal->winding->p;
+ Error( "MAX_MAP_PORTALVERTS (probably a broken areaportal near %.1f %.1f %.1f ", p->x, p->y, p->z );
+ }
+
+ for( i=0; i < nIndices; i++ )
+ {
+ g_ClipPortalVerts[g_nClipPortalVerts] = points[ indices[i] ];
+ ++g_nClipPortalVerts;
+ }
+}
+
+
+// Sets node_t::area for non-leaf nodes (this allows an optimization in the renderer).
+void SetNodeAreaIndices_R( node_t *node )
+{
+ // All leaf area indices should already be set.
+ if( node->planenum == PLANENUM_LEAF )
+ return;
+
+ // Have the children set their area indices.
+ SetNodeAreaIndices_R( node->children[0] );
+ SetNodeAreaIndices_R( node->children[1] );
+
+ // If all children (leaves or nodes) are in the same area, then set our area
+ // to this area as well. Otherwise, set it to -1.
+ if( node->children[0]->area == node->children[1]->area )
+ node->area = node->children[0]->area;
+ else
+ node->area = -1;
+}
+
+
+/*
+=============
+EmitAreaPortals
+
+=============
+*/
+void EmitAreaPortals (node_t *headnode)
+{
+ entity_t *e;
+ dareaportal_t *dp;
+
+ if (c_areas > MAX_MAP_AREAS)
+ Error ("Map is split into too many unique areas (max = %d)\nProbably too many areaportals", MAX_MAP_AREAS);
+ numareas = c_areas+1;
+ numareaportals = 1; // leave 0 as an error
+
+ // Reset the clip portal vert info.
+ g_nClipPortalVerts = 0;
+
+ for (int iSrcArea=1 ; iSrcArea<=c_areas ; iSrcArea++)
+ {
+ dareas[iSrcArea].firstareaportal = numareaportals;
+ for (int j=0 ; j<num_entities ; j++)
+ {
+ e = &entities[j];
+ if (!e->areaportalnum)
+ continue;
+
+ if (e->portalareas[0] == iSrcArea || e->portalareas[1] == iSrcArea)
+ {
+ int iSide = (e->portalareas[0] == iSrcArea);
+
+ // We're only interested in the portal that divides the two areas.
+ // One of the portals that leads into the CONTENTS_AREAPORTAL just bounds
+ // the same two areas but the other bounds two different ones.
+ portal_t *pLeadingPortal = e->m_pPortalsLeadingIntoAreas[0];
+ if( pLeadingPortal->nodes[0]->area == pLeadingPortal->nodes[1]->area )
+ pLeadingPortal = e->m_pPortalsLeadingIntoAreas[1];
+
+ if( pLeadingPortal )
+ {
+ Assert( pLeadingPortal->nodes[0]->area != pLeadingPortal->nodes[1]->area );
+
+ dp = &dareaportals[numareaportals];
+ numareaportals++;
+
+ dp->m_PortalKey = e->areaportalnum;
+ dp->otherarea = e->portalareas[iSide];
+ dp->planenum = pLeadingPortal->onnode->planenum;
+
+ Assert( pLeadingPortal->nodes[0]->planenum == PLANENUM_LEAF );
+ Assert( pLeadingPortal->nodes[1]->planenum == PLANENUM_LEAF );
+
+ if( pLeadingPortal->nodes[0]->area == dp->otherarea )
+ {
+ // Use the flipped version of the plane.
+ dp->planenum = (dp->planenum & ~1) | (~dp->planenum & 1);
+ }
+
+ EmitClipPortalGeometry( headnode, pLeadingPortal, iSrcArea, dp );
+ }
+ }
+ }
+
+ dareas[iSrcArea].numareaportals = numareaportals - dareas[iSrcArea].firstareaportal;
+ }
+
+ SetNodeAreaIndices_R( headnode );
+
+ qprintf ("%5i numareas\n", numareas);
+ qprintf ("%5i numareaportals\n", numareaportals);
+}
+
+/*
+=============
+FloodAreas
+
+Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
+=============
+*/
+void FloodAreas (tree_t *tree)
+{
+ int start = Plat_FloatTime();
+ qprintf ("--- FloodAreas ---\n");
+ Msg("Processing areas...");
+ FindAreas_r (tree->headnode);
+ SetAreaPortalAreas_r (tree, tree->headnode);
+ qprintf ("%5i areas\n", c_areas);
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+}
+
+//======================================================
+
+int c_outside;
+int c_inside;
+int c_solid;
+
+void FillOutside_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FillOutside_r (node->children[0]);
+ FillOutside_r (node->children[1]);
+ return;
+ }
+
+ // anything not reachable by an entity
+ // can be filled away
+ if (!node->occupied)
+ {
+ if (node->contents != CONTENTS_SOLID)
+ {
+ c_outside++;
+ node->contents = CONTENTS_SOLID;
+ }
+ else
+ c_solid++;
+ }
+ else
+ c_inside++;
+
+}
+
+/*
+=============
+FillOutside
+
+Fill all nodes that can't be reached by entities
+=============
+*/
+void FillOutside (node_t *headnode)
+{
+ c_outside = 0;
+ c_inside = 0;
+ c_solid = 0;
+ qprintf ("--- FillOutside ---\n");
+ FillOutside_r (headnode);
+ qprintf ("%5i solid leafs\n", c_solid);
+ qprintf ("%5i leafs filled\n", c_outside);
+ qprintf ("%5i inside leafs\n", c_inside);
+}
+
+
+static float ComputeDistFromPlane( winding_t *pWinding, plane_t *pPlane, float maxdist )
+{
+ float totaldist = 0.0f;
+ for (int i = 0; i < pWinding->numpoints; ++i)
+ {
+ totaldist += fabs(DotProduct( pPlane->normal, pWinding->p[i] ) - pPlane->dist);
+ if (totaldist > maxdist)
+ return totaldist;
+ }
+ return totaldist;
+}
+
+
+//-----------------------------------------------------------------------------
+// Display portal error
+//-----------------------------------------------------------------------------
+static void DisplayPortalError( portal_t *p, int viscontents )
+{
+ char contents[3][1024];
+ PrintBrushContentsToString( p->nodes[0]->contents, contents[0], sizeof( contents[0] ) );
+ PrintBrushContentsToString( p->nodes[1]->contents, contents[1], sizeof( contents[1] ) );
+ PrintBrushContentsToString( viscontents, contents[2], sizeof( contents[2] ) );
+
+ Vector center;
+ WindingCenter( p->winding, center );
+ Warning( "\nFindPortalSide: Couldn't find a good match for which brush to assign to a portal near (%.1f %.1f %.1f)\n", center.x, center.y, center.z);
+ Warning( "Leaf 0 contents: %s\n", contents[0] );
+ Warning( "Leaf 1 contents: %s\n", contents[1] );
+ Warning( "viscontents (node 0 contents ^ node 1 contents): %s\n", contents[2] );
+ Warning( "This means that none of the brushes in leaf 0 or 1 that touches the portal has %s\n", contents[2] );
+ Warning( "Check for a huge brush enclosing the coordinates above that has contents %s\n", contents[2] );
+ Warning( "Candidate brush IDs: " );
+
+ CUtlVector<int> listed;
+ for (int j=0 ; j<2 ; j++)
+ {
+ node_t *n = p->nodes[j];
+ for (bspbrush_t *bb=n->brushlist ; bb ; bb=bb->next)
+ {
+ mapbrush_t *brush = bb->original;
+ if ( brush->contents & viscontents )
+ {
+ if ( listed.Find( brush->brushnum ) == -1 )
+ {
+ listed.AddToTail( brush->brushnum );
+ Warning( "Brush %d: ", brush->id );
+ }
+ }
+ }
+ }
+ Warning( "\n\n" );
+}
+
+
+//==============================================================
+
+/*
+============
+FindPortalSide
+
+Finds a brush side to use for texturing the given portal
+============
+*/
+void FindPortalSide (portal_t *p)
+{
+ int viscontents;
+ bspbrush_t *bb;
+ mapbrush_t *brush;
+ node_t *n;
+ int i,j;
+ int planenum;
+ side_t *side, *bestside;
+ float bestdist;
+ plane_t *p1, *p2;
+
+ // decide which content change is strongest
+ // solid > lava > water, etc
+ viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
+ if (!viscontents)
+ {
+ return;
+ }
+
+ planenum = p->onnode->planenum;
+ bestside = NULL;
+ bestdist = 1000000;
+
+ for (j=0 ; j<2 ; j++)
+ {
+ n = p->nodes[j];
+ p1 = &g_MainMap->mapplanes[p->onnode->planenum];
+
+ for (bb=n->brushlist ; bb ; bb=bb->next)
+ {
+ brush = bb->original;
+ if ( !(brush->contents & viscontents) )
+ continue;
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ side = &brush->original_sides[i];
+ if (side->bevel)
+ continue;
+ if (side->texinfo == TEXINFO_NODE)
+ continue; // non-visible
+
+ if ((side->planenum&~1) == planenum)
+ { // exact match
+ bestside = &brush->original_sides[i];
+ bestdist = 0.0f;
+ goto gotit;
+ }
+
+ p2 = &g_MainMap->mapplanes[side->planenum&~1];
+
+ float dist = ComputeDistFromPlane( p->winding, p2, bestdist );
+ if (dist < bestdist)
+ {
+ bestside = side;
+ bestdist = dist;
+ }
+ }
+ }
+ }
+
+gotit:
+ if (!bestside)
+ qprintf ("WARNING: side not found for portal\n");
+
+ // Compute average dist, check for problems...
+ if ((bestdist / p->winding->numpoints) > 2)
+ {
+ static int nWarnCount = 0;
+ if ( nWarnCount < 8 )
+ {
+ DisplayPortalError( p, viscontents );
+ if ( ++nWarnCount == 8 )
+ {
+ Warning("*** Suppressing further FindPortalSide errors.... ***\n" );
+ }
+ }
+ }
+
+ p->sidefound = true;
+ p->side = bestside;
+}
+
+
+/*
+===============
+MarkVisibleSides_r
+
+===============
+*/
+void MarkVisibleSides_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ MarkVisibleSides_r (node->children[0]);
+ MarkVisibleSides_r (node->children[1]);
+ return;
+ }
+
+ // empty leafs are never boundary leafs
+ if (!node->contents)
+ return;
+
+ // see if there is a visible face
+ for (p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (!p->onnode)
+ continue; // edge of world
+ if (!p->sidefound)
+ FindPortalSide (p);
+ if (p->side)
+ p->side->visible = true;
+ }
+
+}
+
+/*
+=============
+MarkVisibleSides
+
+=============
+*/
+// UNDONE: Put detail brushes in a separate list (not mapbrushes) ?
+void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush, int detailScreen)
+{
+ int i, j;
+ mapbrush_t *mb;
+ int numsides;
+ qboolean detail;
+
+ qprintf ("--- MarkVisibleSides ---\n");
+
+ // clear all the visible flags
+ for (i=startbrush ; i<endbrush ; i++)
+ {
+ mb = &g_MainMap->mapbrushes[i];
+
+ if ( detailScreen != FULL_DETAIL )
+ {
+ qboolean onlyDetail = (detailScreen==ONLY_DETAIL)?true:false;
+ // true for detail brushes
+ detail = (mb->contents & CONTENTS_DETAIL) ? true : false;
+ if ( onlyDetail ^ detail )
+ {
+ // both of these must have the same value or we're not interested in this brush
+ continue;
+ }
+ }
+
+ numsides = mb->numsides;
+ for (j=0 ; j<numsides ; j++)
+ mb->original_sides[j].visible = false;
+ }
+
+ // set visible flags on the sides that are used by portals
+ MarkVisibleSides_r (tree->headnode);
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to determine which sides are visible
+//-----------------------------------------------------------------------------
+void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount )
+{
+ qprintf ("--- MarkVisibleSides ---\n");
+
+ // clear all the visible flags
+ int i, j;
+ for ( i=0; i < nCount; ++i )
+ {
+ mapbrush_t *mb = ppBrushes[i];
+ int numsides = mb->numsides;
+ for (j=0 ; j<numsides ; j++)
+ {
+ mb->original_sides[j].visible = false;
+ }
+ }
+
+ // set visible flags on the sides that are used by portals
+ MarkVisibleSides_r( tree->headnode );
+}
+
diff --git a/mp/src/utils/vbsp/portals.h b/mp/src/utils/vbsp/portals.h new file mode 100644 index 00000000..e8b45c7b --- /dev/null +++ b/mp/src/utils/vbsp/portals.h @@ -0,0 +1,19 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PORTALS_H
+#define PORTALS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// Sets up the g_ClipPortalIndices array.
+void TranslateClipPortalIndices();
+
+
+#endif // PORTALS_H
diff --git a/mp/src/utils/vbsp/prtfile.cpp b/mp/src/utils/vbsp/prtfile.cpp new file mode 100644 index 00000000..c192176b --- /dev/null +++ b/mp/src/utils/vbsp/prtfile.cpp @@ -0,0 +1,374 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "collisionutils.h"
+/*
+==============================================================================
+
+PORTAL FILE GENERATION
+
+Save out name.prt for qvis to read
+==============================================================================
+*/
+
+
+#define PORTALFILE "PRT1"
+
+struct cluster_portals_t
+{
+ CUtlVector<portal_t *> portals;
+};
+
+int num_visclusters; // clusters the player can be in
+int num_visportals;
+
+int g_SkyCluster = -1;
+
+void WriteFloat (FILE *f, vec_t v)
+{
+ if ( fabs(v - RoundInt(v)) < 0.001 )
+ fprintf (f,"%i ",(int)RoundInt(v));
+ else
+ fprintf (f,"%f ",v);
+}
+
+
+/*
+=================
+WritePortalFile_r
+=================
+*/
+void WritePortalFile(FILE *pFile, const CUtlVector<cluster_portals_t> &list)
+{
+ portal_t *p;
+ winding_t *w;
+ Vector normal;
+ vec_t dist;
+
+ for ( int clusterIndex = 0; clusterIndex < list.Count(); clusterIndex++ )
+ {
+ for ( int j = 0; j < list[clusterIndex].portals.Count(); j++ )
+ {
+ p = list[clusterIndex].portals[j];
+ w = p->winding;
+ // write out to the file
+
+ // sometimes planes get turned around when they are very near
+ // the changeover point between different axis. interpret the
+ // plane the same way vis will, and flip the side orders if needed
+ // FIXME: is this still relevent?
+ WindingPlane (w, normal, &dist);
+ if ( DotProduct (p->plane.normal, normal) < 0.99 )
+ { // backwards...
+ fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
+ }
+ else
+ {
+ fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
+ }
+
+ for (int i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pFile,"(");
+ WriteFloat (pFile, w->p[i][0]);
+ WriteFloat (pFile, w->p[i][1]);
+ WriteFloat (pFile, w->p[i][2]);
+ fprintf (pFile,") ");
+ }
+ fprintf (pFile,"\n");
+ }
+ }
+}
+
+struct viscluster_t
+{
+ bspbrush_t *pBrushes;
+ int clusterIndex;
+ int leafCount;
+ int leafStart;
+};
+
+static CUtlVector<viscluster_t> g_VisClusters;
+
+// add to the list of brushes the merge leaves into single vis clusters
+void AddVisCluster( entity_t *pFuncVisCluster )
+{
+ viscluster_t tmp;
+ Vector clipMins, clipMaxs;
+ clipMins[0] = clipMins[1] = clipMins[2] = MIN_COORD_INTEGER;
+ clipMaxs[0] = clipMaxs[1] = clipMaxs[2] = MAX_COORD_INTEGER;
+
+ // build the map brushes out into the minimum non-overlapping set of brushes
+ bspbrush_t *pBSPBrush = MakeBspBrushList( pFuncVisCluster->firstbrush, pFuncVisCluster->firstbrush + pFuncVisCluster->numbrushes,
+ clipMins, clipMaxs, NO_DETAIL);
+ tmp.pBrushes = ChopBrushes( pBSPBrush );
+
+ // store the entry in the list
+ tmp.clusterIndex = -1;
+ tmp.leafCount = 0;
+ tmp.leafStart = 0;
+
+#if DEBUG_VISUALIZE_CLUSTERS
+ int debug = atoi(ValueForKey(pFuncVisCluster,"debug"));
+ if ( debug )
+ {
+ Msg("Debug vis cluster %d\n", g_VisClusters.Count() );
+ }
+#endif
+
+ g_VisClusters.AddToTail( tmp );
+
+ // clear out this entity so it won't get written to the bsp
+ pFuncVisCluster->epairs = NULL;
+ pFuncVisCluster->numbrushes = 0;
+}
+
+// returns the total overlapping volume of intersection between the node and the brush list
+float VolumeOfIntersection( bspbrush_t *pBrushList, node_t *pNode )
+{
+ float volume = 0.0f;
+ for ( bspbrush_t *pBrush = pBrushList; pBrush; pBrush = pBrush->next )
+ {
+ if ( IsBoxIntersectingBox( pNode->mins, pNode->maxs, pBrush->mins, pBrush->maxs ) )
+ {
+ bspbrush_t *pIntersect = IntersectBrush( pNode->volume, pBrush );
+ if ( pIntersect )
+ {
+ volume += BrushVolume( pIntersect );
+ FreeBrush( pIntersect );
+ }
+ }
+ }
+
+ return volume;
+}
+
+// Search for a forced cluster that this node is within
+// NOTE: Returns the first one found, these won't merge themselves together
+int GetVisCluster( node_t *pNode )
+{
+ float maxVolume = BrushVolume(pNode->volume) * 0.10f; // needs to cover at least 10% of the volume to overlap
+ int maxIndex = -1;
+ // UNDONE: This could get slow
+ for ( int i = g_VisClusters.Count(); --i >= 0; )
+ {
+ float volume = VolumeOfIntersection( g_VisClusters[i].pBrushes, pNode );
+ if ( volume > maxVolume )
+ {
+ volume = maxVolume;
+ maxIndex = i;
+ }
+ }
+ return maxIndex;
+}
+/*
+================
+NumberLeafs_r
+================
+*/
+void BuildVisLeafList_r (node_t *node, CUtlVector<node_t *> &leaves)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ { // decision node
+ node->cluster = -99;
+ BuildVisLeafList_r (node->children[0], leaves);
+ BuildVisLeafList_r (node->children[1], leaves);
+ return;
+ }
+
+ if ( node->contents & CONTENTS_SOLID )
+ { // solid block, viewpoint never inside
+ node->cluster = -1;
+ return;
+ }
+ leaves.AddToTail(node);
+}
+
+// Give each leaf in the list of empty leaves a vis cluster number
+// some are within func_viscluster volumes and will be merged together
+// every other leaf gets its own unique number
+void NumberLeafs( const CUtlVector<node_t *> &leaves )
+{
+ for ( int i = 0; i < leaves.Count(); i++ )
+ {
+ node_t *node = leaves[i];
+ int visCluster = GetVisCluster( node );
+ if ( visCluster >= 0 )
+ {
+ if ( g_VisClusters[visCluster].clusterIndex < 0 )
+ {
+ g_VisClusters[visCluster].clusterIndex = num_visclusters;
+ num_visclusters++;
+ }
+ g_VisClusters[visCluster].leafCount++;
+ node->cluster = g_VisClusters[visCluster].clusterIndex;
+ }
+ else
+ {
+ if ( !g_bSkyVis && Is3DSkyboxArea( node->area ) )
+ {
+ if ( g_SkyCluster < 0 )
+ {
+ // allocate a cluster for the sky
+ g_SkyCluster = num_visclusters;
+ num_visclusters++;
+ }
+ node->cluster = g_SkyCluster;
+ }
+ else
+ {
+ node->cluster = num_visclusters;
+ num_visclusters++;
+ }
+ }
+ }
+
+#if DEBUG_VISUALIZE_CLUSTERS
+ for ( int i = 0; i < g_VisClusters.Count(); i++ )
+ {
+ char name[256];
+ sprintf(name, "u:\\main\\game\\ep2\\maps\\vis_%02d.gl", i );
+ FileHandle_t fp = g_pFileSystem->Open( name, "w" );
+ Msg("Writing %s\n", name );
+ for ( bspbrush_t *pBrush = g_VisClusters[i].pBrushes; pBrush; pBrush = pBrush->next )
+ {
+ for (int i = 0; i < pBrush->numsides; i++ )
+ OutputWindingColor( pBrush->sides[i].winding, fp, 0, 255, 0 );
+ }
+ for ( int j = 0; j < leaves.Count(); j++ )
+ {
+ if ( leaves[j]->cluster == g_VisClusters[i].clusterIndex )
+ {
+ bspbrush_t *pBrush = leaves[j]->volume;
+ for (int k = 0; k < pBrush->numsides; k++ )
+ OutputWindingColor( pBrush->sides[k].winding, fp, 64 + (j&31), 64, 64 - (j&31) );
+ }
+ }
+ g_pFileSystem->Close(fp);
+ }
+#endif
+}
+
+// build a list of all vis portals that connect clusters
+int BuildPortalList( CUtlVector<cluster_portals_t> &portalList, const CUtlVector<node_t *> &leaves )
+{
+ int portalCount = 0;
+ for ( int i = 0; i < leaves.Count(); i++ )
+ {
+ node_t *node = leaves[i];
+ // count the portals
+ for (portal_t *p = node->portals ; p ; )
+ {
+ if (p->nodes[0] == node) // only write out from first leaf
+ {
+ if ( p->nodes[0]->cluster != p->nodes[1]->cluster )
+ {
+ if (Portal_VisFlood (p))
+ {
+ portalCount++;
+ portalList[node->cluster].portals.AddToTail(p);
+ }
+ }
+ p = p->next[0];
+ }
+ else
+ p = p->next[1];
+ }
+ }
+ return portalCount;
+}
+
+
+/*
+================
+CreateVisPortals_r
+================
+*/
+void CreateVisPortals_r (node_t *node)
+{
+ // stop as soon as we get to a leaf
+ if (node->planenum == PLANENUM_LEAF )
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ CreateVisPortals_r (node->children[0]);
+ CreateVisPortals_r (node->children[1]);
+}
+
+int clusterleaf;
+void SaveClusters_r (node_t *node)
+{
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ dleafs[clusterleaf++].cluster = node->cluster;
+ return;
+ }
+ SaveClusters_r (node->children[0]);
+ SaveClusters_r (node->children[1]);
+}
+
+/*
+================
+WritePortalFile
+================
+*/
+void WritePortalFile (tree_t *tree)
+{
+ char filename[1024];
+ node_t *headnode;
+ int start = Plat_FloatTime();
+
+ qprintf ("--- WritePortalFile ---\n");
+
+ sprintf (filename, "%s.prt", source);
+ Msg ("writing %s...", filename);
+
+ headnode = tree->headnode;
+
+ FreeTreePortals_r (headnode);
+ MakeHeadnodePortals (tree);
+
+ CreateVisPortals_r (headnode);
+
+// set the cluster field in every leaf and count the total number of portals
+ num_visclusters = 0;
+ Msg("Building visibility clusters...\n");
+ CUtlVector<node_t *> leaves;
+ BuildVisLeafList_r( headnode, leaves );
+
+ NumberLeafs (leaves);
+ CUtlVector<cluster_portals_t> portalList;
+ portalList.SetCount( num_visclusters );
+ num_visportals = BuildPortalList( portalList, leaves );
+// write the file
+ FILE *pf = fopen (filename, "w");
+ if (!pf)
+ Error ("Error opening %s", filename);
+
+ fprintf (pf, "%s\n", PORTALFILE);
+ fprintf (pf, "%i\n", num_visclusters);
+ fprintf (pf, "%i\n", num_visportals);
+
+ qprintf ("%5i visclusters\n", num_visclusters);
+ qprintf ("%5i visportals\n", num_visportals);
+
+ WritePortalFile(pf, portalList);
+
+ fclose (pf);
+
+ // we need to store the clusters out now because ordering
+ // issues made us do this after writebsp...
+ clusterleaf = 1;
+ SaveClusters_r (headnode);
+
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+}
+
diff --git a/mp/src/utils/vbsp/staticprop.cpp b/mp/src/utils/vbsp/staticprop.cpp new file mode 100644 index 00000000..810465b3 --- /dev/null +++ b/mp/src/utils/vbsp/staticprop.cpp @@ -0,0 +1,741 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Places "detail" objects which are client-only renderable things
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "bsplib.h"
+#include "utlvector.h"
+#include "bspfile.h"
+#include "gamebspfile.h"
+#include "VPhysics_Interface.h"
+#include "Studio.h"
+#include "byteswap.h"
+#include "UtlBuffer.h"
+#include "CollisionUtils.h"
+#include <float.h>
+#include "CModel.h"
+#include "PhysDll.h"
+#include "utlsymbol.h"
+#include "tier1/strtools.h"
+#include "KeyValues.h"
+
+static void SetCurrentModel( studiohdr_t *pStudioHdr );
+static void FreeCurrentModelVertexes();
+
+IPhysicsCollision *s_pPhysCollision = NULL;
+
+//-----------------------------------------------------------------------------
+// These puppies are used to construct the game lumps
+//-----------------------------------------------------------------------------
+static CUtlVector<StaticPropDictLump_t> s_StaticPropDictLump;
+static CUtlVector<StaticPropLump_t> s_StaticPropLump;
+static CUtlVector<StaticPropLeafLump_t> s_StaticPropLeafLump;
+
+
+//-----------------------------------------------------------------------------
+// Used to build the static prop
+//-----------------------------------------------------------------------------
+struct StaticPropBuild_t
+{
+ char const* m_pModelName;
+ char const* m_pLightingOrigin;
+ Vector m_Origin;
+ QAngle m_Angles;
+ int m_Solid;
+ int m_Skin;
+ int m_Flags;
+ float m_FadeMinDist;
+ float m_FadeMaxDist;
+ bool m_FadesOut;
+ float m_flForcedFadeScale;
+ unsigned short m_nMinDXLevel;
+ unsigned short m_nMaxDXLevel;
+};
+
+
+//-----------------------------------------------------------------------------
+// Used to cache collision model generation
+//-----------------------------------------------------------------------------
+struct ModelCollisionLookup_t
+{
+ CUtlSymbol m_Name;
+ CPhysCollide* m_pCollide;
+};
+
+static bool ModelLess( ModelCollisionLookup_t const& src1, ModelCollisionLookup_t const& src2 )
+{
+ return src1.m_Name < src2.m_Name;
+}
+
+static CUtlRBTree<ModelCollisionLookup_t, unsigned short> s_ModelCollisionCache( 0, 32, ModelLess );
+static CUtlVector<int> s_LightingInfo;
+
+
+//-----------------------------------------------------------------------------
+// Gets the keyvalues from a studiohdr
+//-----------------------------------------------------------------------------
+bool StudioKeyValues( studiohdr_t* pStudioHdr, KeyValues *pValue )
+{
+ if ( !pStudioHdr )
+ return false;
+
+ return pValue->LoadFromBuffer( pStudioHdr->pszName(), pStudioHdr->KeyValueText() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Makes sure the studio model is a static prop
+//-----------------------------------------------------------------------------
+enum isstaticprop_ret
+{
+ RET_VALID,
+ RET_FAIL_NOT_MARKED_STATIC_PROP,
+ RET_FAIL_DYNAMIC,
+};
+
+isstaticprop_ret IsStaticProp( studiohdr_t* pHdr )
+{
+ if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
+ return RET_FAIL_NOT_MARKED_STATIC_PROP;
+
+ // If it's got a propdata section in the model's keyvalues, it's not allowed to be a prop_static
+ KeyValues *modelKeyValues = new KeyValues(pHdr->pszName());
+ if ( StudioKeyValues( pHdr, modelKeyValues ) )
+ {
+ KeyValues *sub = modelKeyValues->FindKey("prop_data");
+ if ( sub )
+ {
+ if ( !(sub->GetInt( "allowstatic", 0 )) )
+ {
+ modelKeyValues->deleteThis();
+ return RET_FAIL_DYNAMIC;
+ }
+ }
+ }
+ modelKeyValues->deleteThis();
+
+ return RET_VALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add static prop model to the list of models
+//-----------------------------------------------------------------------------
+
+static int AddStaticPropDictLump( char const* pModelName )
+{
+ StaticPropDictLump_t dictLump;
+ strncpy( dictLump.m_Name, pModelName, DETAIL_NAME_LENGTH );
+
+ for (int i = s_StaticPropDictLump.Size(); --i >= 0; )
+ {
+ if (!memcmp(&s_StaticPropDictLump[i], &dictLump, sizeof(dictLump) ))
+ return i;
+ }
+
+ return s_StaticPropDictLump.AddToTail( dictLump );
+}
+
+
+//-----------------------------------------------------------------------------
+// Load studio model vertex data from a file...
+//-----------------------------------------------------------------------------
+bool LoadStudioModel( char const* pModelName, char const* pEntityType, CUtlBuffer& buf )
+{
+ if ( !g_pFullFileSystem->ReadFile( pModelName, NULL, buf ) )
+ return false;
+
+ // Check that it's valid
+ if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
+ strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
+ {
+ return false;
+ }
+
+ studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
+
+ Studio_ConvertStudioHdrToNewVersion( pHdr );
+
+ if (pHdr->version != STUDIO_VERSION)
+ {
+ return false;
+ }
+
+ isstaticprop_ret isStaticProp = IsStaticProp(pHdr);
+ if ( isStaticProp != RET_VALID )
+ {
+ if ( isStaticProp == RET_FAIL_NOT_MARKED_STATIC_PROP )
+ {
+ Warning("Error! To use model \"%s\"\n"
+ " with %s, it must be compiled with $staticprop!\n", pModelName, pEntityType );
+ }
+ else if ( isStaticProp == RET_FAIL_DYNAMIC )
+ {
+ Warning("Error! %s using model \"%s\", which must be used on a dynamic entity (i.e. prop_physics). Deleted.\n", pEntityType, pModelName );
+ }
+ return false;
+ }
+
+ // ensure reset
+ pHdr->pVertexBase = NULL;
+ pHdr->pIndexBase = NULL;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from a studio mesh
+//-----------------------------------------------------------------------------
+static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh )
+{
+ // Generate a list of all verts in the mesh
+ Vector** ppVerts = (Vector**)stackalloc(pMesh->numvertices * sizeof(Vector*) );
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData();
+ Assert( vertData ); // This can only return NULL on X360 for now
+ for (int i = 0; i < pMesh->numvertices; ++i)
+ {
+ ppVerts[i] = vertData->Position(i);
+ }
+
+ // Generate a convex hull from the verts
+ return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from the studio model
+//-----------------------------------------------------------------------------
+CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
+{
+ CUtlVector<CPhysConvex*> convexHulls;
+
+ for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
+ {
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
+ for( int model = 0; model < pBodyPart->nummodels; ++model )
+ {
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
+ for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
+ {
+ // Make a convex hull for each mesh
+ // NOTE: This won't work unless the model has been compiled
+ // with $staticprop
+ mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
+ convexHulls.AddToTail( ComputeConvexHull( pStudioMesh ) );
+ }
+ }
+ }
+
+ // Convert an array of convex elements to a compiled collision model
+ // (this deletes the convex elements)
+ return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Add, find collision model in cache
+//-----------------------------------------------------------------------------
+static CPhysCollide* GetCollisionModel( char const* pModelName )
+{
+ // Convert to a common string
+ char* pTemp = (char*)_alloca(strlen(pModelName) + 1);
+ strcpy( pTemp, pModelName );
+ _strlwr( pTemp );
+
+ char* pSlash = strchr( pTemp, '\\' );
+ while( pSlash )
+ {
+ *pSlash = '/';
+ pSlash = strchr( pTemp, '\\' );
+ }
+
+ // Find it in the cache
+ ModelCollisionLookup_t lookup;
+ lookup.m_Name = pTemp;
+ int i = s_ModelCollisionCache.Find( lookup );
+ if (i != s_ModelCollisionCache.InvalidIndex())
+ return s_ModelCollisionCache[i].m_pCollide;
+
+ // Load the studio model file
+ CUtlBuffer buf;
+ if (!LoadStudioModel(pModelName, "prop_static", buf))
+ {
+ Warning("Error loading studio model \"%s\"!\n", pModelName );
+
+ // This way we don't try to load it multiple times
+ lookup.m_pCollide = 0;
+ s_ModelCollisionCache.Insert( lookup );
+
+ return 0;
+ }
+
+ // Compute the convex hull of the model...
+ studiohdr_t* pStudioHdr = (studiohdr_t*)buf.PeekGet();
+
+ // necessary for vertex access
+ SetCurrentModel( pStudioHdr );
+
+ lookup.m_pCollide = ComputeConvexHull( pStudioHdr );
+ s_ModelCollisionCache.Insert( lookup );
+
+ if ( !lookup.m_pCollide )
+ {
+ Warning("Bad geometry on \"%s\"!\n", pModelName );
+ }
+
+ // Debugging
+ if (g_DumpStaticProps)
+ {
+ static int propNum = 0;
+ char tmp[128];
+ sprintf( tmp, "staticprop%03d.txt", propNum );
+ DumpCollideToGlView( lookup.m_pCollide, tmp );
+ ++propNum;
+ }
+
+ FreeCurrentModelVertexes();
+
+ // Insert into cache...
+ return lookup.m_pCollide;
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests a single leaf against the static prop
+//-----------------------------------------------------------------------------
+
+static bool TestLeafAgainstCollide( int depth, int* pNodeList,
+ Vector const& origin, QAngle const& angles, CPhysCollide* pCollide )
+{
+ // Copy the planes in the node list into a list of planes
+ float* pPlanes = (float*)_alloca(depth * 4 * sizeof(float) );
+ int idx = 0;
+ for (int i = depth; --i >= 0; ++idx )
+ {
+ int sign = (pNodeList[i] < 0) ? -1 : 1;
+ int node = (sign < 0) ? - pNodeList[i] - 1 : pNodeList[i];
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ pPlanes[idx*4] = sign * pPlane->normal[0];
+ pPlanes[idx*4+1] = sign * pPlane->normal[1];
+ pPlanes[idx*4+2] = sign * pPlane->normal[2];
+ pPlanes[idx*4+3] = sign * pPlane->dist;
+ }
+
+ // Make a convex solid out of the planes
+ CPhysConvex* pPhysConvex = s_pPhysCollision->ConvexFromPlanes( pPlanes, depth, 0.0f );
+
+ // This should never happen, but if it does, return no collision
+ Assert( pPhysConvex );
+ if (!pPhysConvex)
+ return false;
+
+ CPhysCollide* pLeafCollide = s_pPhysCollision->ConvertConvexToCollide( &pPhysConvex, 1 );
+
+ // Collide the leaf solid with the static prop solid
+ trace_t tr;
+ s_pPhysCollision->TraceCollide( vec3_origin, vec3_origin, pLeafCollide, vec3_angle,
+ pCollide, origin, angles, &tr );
+
+ s_pPhysCollision->DestroyCollide( pLeafCollide );
+
+ return (tr.startsolid != 0);
+}
+
+//-----------------------------------------------------------------------------
+// Find all leaves that intersect with this bbox + test against the static prop..
+//-----------------------------------------------------------------------------
+
+static void ComputeConvexHullLeaves_R( int node, int depth, int* pNodeList,
+ Vector const& mins, Vector const& maxs,
+ Vector const& origin, QAngle const& angles, CPhysCollide* pCollide,
+ CUtlVector<unsigned short>& leafList )
+{
+ Assert( pNodeList && pCollide );
+ Vector cornermin, cornermax;
+
+ while( node >= 0 )
+ {
+ dnode_t* pNode = &dnodes[node];
+ dplane_t* pPlane = &dplanes[pNode->planenum];
+
+ // Arbitrary split plane here
+ for (int i = 0; i < 3; ++i)
+ {
+ if (pPlane->normal[i] >= 0)
+ {
+ cornermin[i] = mins[i];
+ cornermax[i] = maxs[i];
+ }
+ else
+ {
+ cornermin[i] = maxs[i];
+ cornermax[i] = mins[i];
+ }
+ }
+
+ if (DotProduct( pPlane->normal, cornermax ) <= pPlane->dist)
+ {
+ // Add the node to the list of nodes
+ pNodeList[depth] = node;
+ ++depth;
+
+ node = pNode->children[1];
+ }
+ else if (DotProduct( pPlane->normal, cornermin ) >= pPlane->dist)
+ {
+ // In this case, we are going in front of the plane. That means that
+ // this plane must have an outward normal facing in the oppisite direction
+ // We indicate this be storing a negative node index in the node list
+ pNodeList[depth] = - node - 1;
+ ++depth;
+
+ node = pNode->children[0];
+ }
+ else
+ {
+ // Here the box is split by the node. First, we'll add the plane as if its
+ // outward facing normal is in the direction of the node plane, then
+ // we'll have to reverse it for the other child...
+ pNodeList[depth] = node;
+ ++depth;
+
+ ComputeConvexHullLeaves_R( pNode->children[1],
+ depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
+
+ pNodeList[depth - 1] = - node - 1;
+ ComputeConvexHullLeaves_R( pNode->children[0],
+ depth, pNodeList, mins, maxs, origin, angles, pCollide, leafList );
+ return;
+ }
+ }
+
+ Assert( pNodeList && pCollide );
+
+ // Never add static props to solid leaves
+ if ( (dleafs[-node-1].contents & CONTENTS_SOLID) == 0 )
+ {
+ if (TestLeafAgainstCollide( depth, pNodeList, origin, angles, pCollide ))
+ {
+ leafList.AddToTail( -node - 1 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Places Static Props in the level
+//-----------------------------------------------------------------------------
+
+static void ComputeStaticPropLeaves( CPhysCollide* pCollide, Vector const& origin,
+ QAngle const& angles, CUtlVector<unsigned short>& leafList )
+{
+ // Compute an axis-aligned bounding box for the collide
+ Vector mins, maxs;
+ s_pPhysCollision->CollideGetAABB( &mins, &maxs, pCollide, origin, angles );
+
+ // Find all leaves that intersect with the bounds
+ int tempNodeList[1024];
+ ComputeConvexHullLeaves_R( 0, 0, tempNodeList, mins, maxs,
+ origin, angles, pCollide, leafList );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the lighting origin
+//-----------------------------------------------------------------------------
+static bool ComputeLightingOrigin( StaticPropBuild_t const& build, Vector& lightingOrigin )
+{
+ for (int i = s_LightingInfo.Count(); --i >= 0; )
+ {
+ int entIndex = s_LightingInfo[i];
+
+ // Check against all lighting info entities
+ char const* pTargetName = ValueForKey( &entities[entIndex], "targetname" );
+ if (!Q_strcmp(pTargetName, build.m_pLightingOrigin))
+ {
+ GetVectorForKey( &entities[entIndex], "origin", lightingOrigin );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Static Props in the level
+//-----------------------------------------------------------------------------
+static void AddStaticPropToLump( StaticPropBuild_t const& build )
+{
+ // Get the collision model
+ CPhysCollide* pConvexHull = GetCollisionModel( build.m_pModelName );
+ if (!pConvexHull)
+ return;
+
+ // Compute the leaves the static prop's convex hull hits
+ CUtlVector< unsigned short > leafList;
+ ComputeStaticPropLeaves( pConvexHull, build.m_Origin, build.m_Angles, leafList );
+
+ if ( !leafList.Count() )
+ {
+ Warning( "Static prop %s outside the map (%.2f, %.2f, %.2f)\n", build.m_pModelName, build.m_Origin.x, build.m_Origin.y, build.m_Origin.z );
+ return;
+ }
+ // Insert an element into the lump data...
+ int i = s_StaticPropLump.AddToTail( );
+ StaticPropLump_t& propLump = s_StaticPropLump[i];
+ propLump.m_PropType = AddStaticPropDictLump( build.m_pModelName );
+ VectorCopy( build.m_Origin, propLump.m_Origin );
+ VectorCopy( build.m_Angles, propLump.m_Angles );
+ propLump.m_FirstLeaf = s_StaticPropLeafLump.Count();
+ propLump.m_LeafCount = leafList.Count();
+ propLump.m_Solid = build.m_Solid;
+ propLump.m_Skin = build.m_Skin;
+ propLump.m_Flags = build.m_Flags;
+ if (build.m_FadesOut)
+ {
+ propLump.m_Flags |= STATIC_PROP_FLAG_FADES;
+ }
+ propLump.m_FadeMinDist = build.m_FadeMinDist;
+ propLump.m_FadeMaxDist = build.m_FadeMaxDist;
+ propLump.m_flForcedFadeScale = build.m_flForcedFadeScale;
+ propLump.m_nMinDXLevel = build.m_nMinDXLevel;
+ propLump.m_nMaxDXLevel = build.m_nMaxDXLevel;
+
+ if (build.m_pLightingOrigin && *build.m_pLightingOrigin)
+ {
+ if (ComputeLightingOrigin( build, propLump.m_LightingOrigin ))
+ {
+ propLump.m_Flags |= STATIC_PROP_USE_LIGHTING_ORIGIN;
+ }
+ }
+
+ // Add the leaves to the leaf lump
+ for (int j = 0; j < leafList.Size(); ++j)
+ {
+ StaticPropLeafLump_t insert;
+ insert.m_Leaf = leafList[j];
+ s_StaticPropLeafLump.AddToTail( insert );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Places static props in the lump
+//-----------------------------------------------------------------------------
+
+static void SetLumpData( )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(GAMELUMP_STATIC_PROPS);
+ if (handle != g_GameLumps.InvalidGameLump())
+ g_GameLumps.DestroyGameLump(handle);
+
+ int dictsize = s_StaticPropDictLump.Size() * sizeof(StaticPropDictLump_t);
+ int objsize = s_StaticPropLump.Size() * sizeof(StaticPropLump_t);
+ int leafsize = s_StaticPropLeafLump.Size() * sizeof(StaticPropLeafLump_t);
+ int size = dictsize + objsize + leafsize + 3 * sizeof(int);
+
+ handle = g_GameLumps.CreateGameLump( GAMELUMP_STATIC_PROPS, size, 0, GAMELUMP_STATIC_PROPS_VERSION );
+
+ // Serialize the data
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size );
+ buf.PutInt( s_StaticPropDictLump.Size() );
+ if (dictsize)
+ buf.Put( s_StaticPropDictLump.Base(), dictsize );
+ buf.PutInt( s_StaticPropLeafLump.Size() );
+ if (leafsize)
+ buf.Put( s_StaticPropLeafLump.Base(), leafsize );
+ buf.PutInt( s_StaticPropLump.Size() );
+ if (objsize)
+ buf.Put( s_StaticPropLump.Base(), objsize );
+}
+
+
+//-----------------------------------------------------------------------------
+// Places Static Props in the level
+//-----------------------------------------------------------------------------
+
+void EmitStaticProps()
+{
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( physicsFactory )
+ {
+ s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ if( !s_pPhysCollision )
+ return;
+ }
+
+ // Generate a list of lighting origins, and strip them out
+ int i;
+ for ( i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!Q_strcmp(pEntity, "info_lighting"))
+ {
+ s_LightingInfo.AddToTail(i);
+ }
+ }
+
+ // Emit specifically specified static props
+ for ( i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "static_prop") || !strcmp(pEntity, "prop_static"))
+ {
+ StaticPropBuild_t build;
+
+ GetVectorForKey( &entities[i], "origin", build.m_Origin );
+ GetAnglesForKey( &entities[i], "angles", build.m_Angles );
+ build.m_pModelName = ValueForKey( &entities[i], "model" );
+ build.m_Solid = IntForKey( &entities[i], "solid" );
+ build.m_Skin = IntForKey( &entities[i], "skin" );
+ build.m_FadeMaxDist = FloatForKey( &entities[i], "fademaxdist" );
+ build.m_Flags = 0;//IntForKey( &entities[i], "spawnflags" ) & STATIC_PROP_WC_MASK;
+ if (IntForKey( &entities[i], "ignorenormals" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_IGNORE_NORMALS;
+ }
+ if (IntForKey( &entities[i], "disableshadows" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_NO_SHADOW;
+ }
+ if (IntForKey( &entities[i], "disablevertexlighting" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_NO_PER_VERTEX_LIGHTING;
+ }
+ if (IntForKey( &entities[i], "disableselfshadowing" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_NO_SELF_SHADOWING;
+ }
+
+ if (IntForKey( &entities[i], "screenspacefade" ) == 1)
+ {
+ build.m_Flags |= STATIC_PROP_SCREEN_SPACE_FADE;
+ }
+
+ const char *pKey = ValueForKey( &entities[i], "fadescale" );
+ if ( pKey && pKey[0] )
+ {
+ build.m_flForcedFadeScale = FloatForKey( &entities[i], "fadescale" );
+ }
+ else
+ {
+ build.m_flForcedFadeScale = 1;
+ }
+ build.m_FadesOut = (build.m_FadeMaxDist > 0);
+ build.m_pLightingOrigin = ValueForKey( &entities[i], "lightingorigin" );
+ if (build.m_FadesOut)
+ {
+ build.m_FadeMinDist = FloatForKey( &entities[i], "fademindist" );
+ if (build.m_FadeMinDist < 0)
+ {
+ build.m_FadeMinDist = build.m_FadeMaxDist;
+ }
+ }
+ else
+ {
+ build.m_FadeMinDist = 0;
+ }
+ build.m_nMinDXLevel = (unsigned short)IntForKey( &entities[i], "mindxlevel" );
+ build.m_nMaxDXLevel = (unsigned short)IntForKey( &entities[i], "maxdxlevel" );
+ AddStaticPropToLump( build );
+
+ // strip this ent from the .bsp file
+ entities[i].epairs = 0;
+ }
+ }
+
+ // Strip out lighting origins; has to be done here because they are used when
+ // static props are made
+ for ( i = s_LightingInfo.Count(); --i >= 0; )
+ {
+ // strip this ent from the .bsp file
+ entities[s_LightingInfo[i]].epairs = 0;
+ }
+
+
+ SetLumpData( );
+}
+
+static studiohdr_t *g_pActiveStudioHdr;
+static void SetCurrentModel( studiohdr_t *pStudioHdr )
+{
+ // track the correct model
+ g_pActiveStudioHdr = pStudioHdr;
+}
+
+static void FreeCurrentModelVertexes()
+{
+ Assert( g_pActiveStudioHdr );
+
+ if ( g_pActiveStudioHdr->pVertexBase )
+ {
+ free( g_pActiveStudioHdr->pVertexBase );
+ g_pActiveStudioHdr->pVertexBase = NULL;
+ }
+}
+
+const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData )
+{
+ char fileName[260];
+ FileHandle_t fileHandle;
+ vertexFileHeader_t *pVvdHdr;
+
+ Assert( pModelData == NULL );
+ Assert( g_pActiveStudioHdr );
+
+ if ( g_pActiveStudioHdr->pVertexBase )
+ {
+ return (vertexFileHeader_t *)g_pActiveStudioHdr->pVertexBase;
+ }
+
+ // mandatory callback to make requested data resident
+ // load and persist the vertex file
+ strcpy( fileName, "models/" );
+ strcat( fileName, g_pActiveStudioHdr->pszName() );
+ Q_StripExtension( fileName, fileName, sizeof( fileName ) );
+ strcat( fileName, ".vvd" );
+
+ // load the model
+ fileHandle = g_pFileSystem->Open( fileName, "rb" );
+ if ( !fileHandle )
+ {
+ Error( "Unable to load vertex data \"%s\"\n", fileName );
+ }
+
+ // Get the file size
+ int size = g_pFileSystem->Size( fileHandle );
+ if (size == 0)
+ {
+ g_pFileSystem->Close( fileHandle );
+ Error( "Bad size for vertex data \"%s\"\n", fileName );
+ }
+
+ pVvdHdr = (vertexFileHeader_t *)malloc(size);
+ g_pFileSystem->Read( pVvdHdr, size, fileHandle );
+ g_pFileSystem->Close( fileHandle );
+
+ // check header
+ if (pVvdHdr->id != MODEL_VERTEX_FILE_ID)
+ {
+ Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
+ }
+ if (pVvdHdr->version != MODEL_VERTEX_FILE_VERSION)
+ {
+ Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
+ }
+ if (pVvdHdr->checksum != g_pActiveStudioHdr->checksum)
+ {
+ Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, g_pActiveStudioHdr->checksum);
+ }
+
+ g_pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
+ return pVvdHdr;
+}
+
diff --git a/mp/src/utils/vbsp/textures.cpp b/mp/src/utils/vbsp/textures.cpp new file mode 100644 index 00000000..fc2034eb --- /dev/null +++ b/mp/src/utils/vbsp/textures.cpp @@ -0,0 +1,737 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "utilmatlib.h"
+#include "physdll.h"
+#include <assert.h>
+#include <malloc.h>
+#include "tier1/strtools.h"
+#include "materialpatch.h"
+#include "KeyValues.h"
+
+void LoadSurfaceProperties( void );
+
+IPhysicsSurfaceProps *physprops = NULL;
+
+int nummiptex;
+textureref_t textureref[MAX_MAP_TEXTURES];
+
+bool g_bHasWater = false;
+
+extern qboolean onlyents;
+
+dtexdata_t *GetTexData( int index )
+{
+ if ( index < 0 )
+ return NULL;
+ Assert( !onlyents );
+ return &dtexdata[ index ];
+}
+
+static qboolean StringIsTrue( const char *str )
+{
+ if( Q_strcasecmp( str, "true" ) == 0 )
+ {
+ return true;
+ }
+ if( Q_strcasecmp( str, "1" ) == 0 )
+ {
+ return true;
+ }
+ return false;
+}
+
+int FindMiptex (const char *name)
+{
+ int i;
+ MaterialSystemMaterial_t matID;
+ const char *propVal, *propVal2;
+ int opacity;
+ bool found;
+
+ for (i=0 ; i<nummiptex ; i++)
+ {
+ if (!strcmp (name, textureref[i].name))
+ {
+ return i;
+ }
+ }
+ if (nummiptex == MAX_MAP_TEXTURES)
+ Error ("Too many unique textures, max %d", MAX_MAP_TEXTURES);
+ strcpy (textureref[i].name, name);
+
+ textureref[i].lightmapWorldUnitsPerLuxel = 0.0f;
+ textureref[i].flags = 0;
+ textureref[i].contents = 0;
+
+ matID = FindOriginalMaterial( name, &found );
+ if( matID == MATERIAL_NOT_FOUND )
+ {
+ return 0;
+ }
+
+ if (!found)
+ Warning("Material not found!: %s\n", name );
+
+ // HANDLE ALL OF THE STUFF THAT ISN'T RENDERED WITH THE MATERIAL THAT IS ONE IT.
+
+ // handle sky
+ if( ( propVal = GetMaterialVar( matID, "%compileSky" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_SKY | SURF_NOLIGHT;
+ }
+ else if( ( propVal = GetMaterialVar( matID, "%compile2DSky" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_SKY | SURF_SKY2D | SURF_NOLIGHT;
+ }
+ // handle hint brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileHint" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_HINT;
+ }
+ // handle skip faces
+ else if ( ( propVal = GetMaterialVar( matID, "%compileSkip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT | SURF_SKIP;
+ }
+ // handle origin brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileOrigin" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_ORIGIN | CONTENTS_DETAIL;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ // handle clip brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileClip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ else if ( ( propVal = GetMaterialVar( matID, "%playerClip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_PLAYERCLIP;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ // handle npc clip brushes
+ else if ( ( propVal = GetMaterialVar( matID, "%compileNpcClip" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_MONSTERCLIP;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+ // handle surface lights which are meant to
+ else if ( ( propVal = GetMaterialVar( matID, "%compileNoChop" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NOCHOP;
+ }
+ // handle triggers
+ else if ( ( propVal = GetMaterialVar( matID, "%compileTrigger" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= ( SURF_NOLIGHT | SURF_TRIGGER );
+ if ( g_NodrawTriggers )
+ {
+ textureref[i].flags |= SURF_NODRAW;
+ }
+ }
+ // handle nolight surfs (except water)
+ else if ( (( propVal = GetMaterialVar( matID, "%compileNoLight" ) ) && StringIsTrue( propVal )) &&
+ !(( propVal2 = GetMaterialVar( matID, "%compileWater" ) ) && StringIsTrue( propVal2 ) ) )
+ {
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+ else
+ {
+ // HANDLE ALL OF THE STUFF THAT IS RENDERED WITH THE MATERIAL THAT IS ON IT.
+
+ // Handle ladders.
+ if ( ( propVal = GetMaterialVar( matID, "%compileLadder" ) ) && StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_LADDER;
+ }
+
+ // handle wet materials
+ if ( ( propVal = GetMaterialVar( matID, "%noPortal" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].flags |= SURF_NOPORTAL;
+ }
+
+ if ( ( propVal = GetMaterialVar( matID, "%compilePassBullets" ) ) && StringIsTrue( propVal ) )
+ {
+ // change contents to grate, so bullets pass through
+ // NOTE: This has effects on visibility too!
+ textureref[i].contents &= ~CONTENTS_SOLID;
+ textureref[i].contents |= CONTENTS_GRATE;
+ }
+
+ if( g_BumpAll || GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_BUMPED_LIGHTMAPS ) )
+ {
+ textureref[i].flags |= SURF_BUMPLIGHT;
+ }
+
+ if( GetMaterialShaderPropertyBool( matID, UTILMATLIB_NEEDS_LIGHTMAP ) )
+ {
+ textureref[i].flags &= ~SURF_NOLIGHT;
+ }
+ else if( !g_bLightIfMissing )
+ {
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+ // handle nodraw faces/brushes
+ if ( ( propVal = GetMaterialVar( matID, "%compileNoDraw" ) ) && StringIsTrue( propVal ) )
+ {
+ // textureref[i].contents |= CONTENTS_DETAIL;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+
+ // Just a combination of nodraw + pass bullets, makes things easier
+ if ( ( propVal = GetMaterialVar( matID, "%compileInvisible" ) ) && StringIsTrue( propVal ) )
+ {
+ // change contents to grate, so bullets pass through
+ // NOTE: This has effects on visibility too!
+ textureref[i].contents &= ~CONTENTS_SOLID;
+ textureref[i].contents |= CONTENTS_GRATE;
+ textureref[i].flags |= SURF_NODRAW | SURF_NOLIGHT;
+ }
+
+ bool checkWindow = true;
+ // handle non solid
+ if ( ( propVal = GetMaterialVar( matID, "%compileNonsolid" ) ) && StringIsTrue( propVal ) )
+ {
+ textureref[i].contents = CONTENTS_OPAQUE;
+ // Non-Solid can't be a window either!
+ checkWindow = false;
+ }
+ // handle block LOS
+ if ( ( propVal = GetMaterialVar( matID, "%compileBlockLOS" ) ) && StringIsTrue( propVal ) )
+ {
+ textureref[i].contents = CONTENTS_BLOCKLOS;
+
+ // BlockLOS can't be a window either!
+ checkWindow = false;
+ }
+
+ if ( ( propVal = GetMaterialVar( matID, "%compileDetail" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents |= CONTENTS_DETAIL;
+ }
+
+ bool bKeepLighting = ( ( propVal = GetMaterialVar( matID, "%compileKeepLight" ) ) &&
+ StringIsTrue( propVal ) );
+
+ // handle materials that want to be treated as water.
+ if ( ( propVal = GetMaterialVar( matID, "%compileWater" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
+ textureref[i].contents |= CONTENTS_WATER;
+ textureref[i].flags |= SURF_WARP | SURF_NOSHADOWS | SURF_NODECALS;
+
+ if ( g_DisableWaterLighting && !bKeepLighting )
+ {
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+
+ // Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
+ g_bHasWater = true;
+ }
+ const char *pShaderName = GetMaterialShaderName(matID);
+ if ( !bKeepLighting && !Q_strncasecmp( pShaderName, "water", 5 ) || !Q_strncasecmp( pShaderName, "UnlitGeneric", 12 ) )
+ {
+ //if ( !(textureref[i].flags & SURF_NOLIGHT) )
+ // Warning("Forcing lit materal %s to nolight\n", name );
+ textureref[i].flags |= SURF_NOLIGHT;
+ }
+
+ if ( ( propVal = GetMaterialVar( matID, "%compileSlime" ) ) &&
+ StringIsTrue( propVal ) )
+ {
+ textureref[i].contents &= ~(CONTENTS_SOLID|CONTENTS_DETAIL);
+ textureref[i].contents |= CONTENTS_SLIME;
+ textureref[i].flags |= SURF_NODECALS;
+ // Set this so that we can check at the end of the process the presence of a a WaterLODControl entity.
+ g_bHasWater = true;
+ }
+
+ opacity = GetMaterialShaderPropertyInt( matID, UTILMATLIB_OPACITY );
+
+ if ( checkWindow && opacity != UTILMATLIB_OPAQUE )
+ {
+ // transparent *and solid* brushes that aren't grates or water must be windows
+ if ( !(textureref[i].contents & (CONTENTS_GRATE|CONTENTS_WATER)) )
+ {
+ textureref[i].contents |= CONTENTS_WINDOW;
+ }
+
+ textureref[i].contents &= ~CONTENTS_SOLID;
+
+ // this affects engine primitive sorting, SURF_TRANS means sort as a translucent primitive
+ if ( opacity == UTILMATLIB_TRANSLUCENT )
+ {
+ textureref[i].flags |= SURF_TRANS;
+ }
+
+ }
+ if ( textureref[i].flags & SURF_NOLIGHT )
+ {
+ textureref[i].flags &= ~SURF_BUMPLIGHT;
+ }
+ }
+
+ nummiptex++;
+
+ return i;
+}
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+Vector baseaxis[18] =
+{
+ Vector(0,0,1), Vector(1,0,0), Vector(0,-1,0), // floor
+ Vector(0,0,-1), Vector(1,0,0), Vector(0,-1,0), // ceiling
+ Vector(1,0,0), Vector(0,1,0), Vector(0,0,-1), // west wall
+ Vector(-1,0,0), Vector(0,1,0), Vector(0,0,-1), // east wall
+ Vector(0,1,0), Vector(1,0,0), Vector(0,0,-1), // south wall
+ Vector(0,-1,0), Vector(1,0,0), Vector(0,0,-1) // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, Vector& xv, Vector& yv)
+{
+ int bestaxis;
+ vec_t dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if (dot > best)
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+
+int g_SurfaceProperties[MAX_MAP_TEXDATA];
+
+
+int GetSurfaceProperties( MaterialSystemMaterial_t matID, const char *pMatName )
+{
+ const char *pPropString = NULL;
+ int surfaceIndex = -1;
+
+ if ( physprops )
+ {
+ pPropString = GetMaterialVar( matID, "$surfaceprop" );
+ if ( pPropString )
+ {
+ surfaceIndex = physprops->GetSurfaceIndex( pPropString );
+ if ( surfaceIndex < 0 )
+ {
+ Msg("Can't find surfaceprop %s for material %s, using default\n", pPropString, pMatName );
+ surfaceIndex = physprops->GetSurfaceIndex( pPropString );
+ surfaceIndex = physprops->GetSurfaceIndex( "default" );
+ }
+ }
+ }
+
+ return surfaceIndex;
+}
+
+int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName )
+{
+ const char *pPropString = NULL;
+ int surfaceIndex = -1;
+
+ if ( physprops )
+ {
+ pPropString = GetMaterialVar( matID, "$surfaceprop2" );
+ if ( pPropString )
+ {
+ surfaceIndex = physprops->GetSurfaceIndex( pPropString );
+ if ( surfaceIndex < 0 )
+ {
+ Msg("Can't find surfacepropblend %s for material %s, using default\n", pPropString, pMatName );
+ surfaceIndex = physprops->GetSurfaceIndex( "default" );
+ }
+ }
+ else
+ {
+ // No surface property 2.
+ return -1;
+ }
+ }
+
+ return surfaceIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds or adds a texdata for the specified name ( same as below except
+// instead of finding the named texture, copies the settings from the passed
+// in sourceTexture. )
+// Used for creation of one off .vmt files for water surface textures
+// Input : *pName - texture name
+// Output : int index into dtexdata array
+//-----------------------------------------------------------------------------
+int FindAliasedTexData( const char *pName_, dtexdata_t *sourceTexture )
+{
+ char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
+ strcpy( pName, pName_ );
+ strlwr( pName );
+ int i, output;
+ bool found;
+ dtexdata_t *pTexData;
+ MaterialSystemMaterial_t matID;
+
+ for ( i = 0; i < numtexdata; i++ )
+ {
+ if ( !strcmp( pName, TexDataStringTable_GetString( GetTexData( i )->nameStringTableID ) ) )
+ return i;
+ }
+
+
+ output = numtexdata;
+ if ( numtexdata >= MAX_MAP_TEXDATA )
+ {
+ Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
+ }
+ pTexData = GetTexData( output );
+ numtexdata++;
+
+ // Save the name of the material.
+ pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
+
+ // Get the width, height, view_width, view_height, and reflectivity from the material system.
+ matID = FindOriginalMaterial( TexDataStringTable_GetString( sourceTexture->nameStringTableID ), &found, false );
+ if( matID == MATERIAL_NOT_FOUND || (!found) )
+ {
+ qprintf( "WARNING: material not found: \"%s\"\n", pName );
+ return -1;
+ }
+
+ GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
+ pTexData->view_width = pTexData->width; // undone: what is this?
+ pTexData->view_height = pTexData->height; // undone: what is this?
+
+ GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
+ g_SurfaceProperties[output] = GetSurfaceProperties( matID, pName );
+
+ return output;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a texdata for the specified name, returns -1 if not found
+//-----------------------------------------------------------------------------
+int FindTexData( const char *pName )
+{
+ // Make sure the texdata doesn't already exist.
+ for( int i = 0; i < numtexdata; i++ )
+ {
+ char const *pTexDataName = TexDataStringTable_GetString( GetTexData( i )->nameStringTableID );
+ if ( !Q_stricmp( pTexDataName, pName ) )
+ return i;
+ }
+ return -1;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Finds or adds a texdata for the specified name
+// Input : *pName - texture name
+// Output : int index into dtexdata array
+//-----------------------------------------------------------------------------
+int FindOrCreateTexData( const char *pName_ )
+{
+ char *pName = ( char * )_alloca( strlen( pName_ ) + 1 );
+ strcpy( pName, pName_ );
+
+ int nOutput = FindTexData( pName );
+ if ( nOutput >= 0 )
+ return nOutput;
+
+ // Didn't find it, add a new one
+ nOutput = numtexdata;
+ if ( numtexdata >= MAX_MAP_TEXDATA )
+ {
+ Error( "Too many unique texture mappings, max = %d\n", MAX_MAP_TEXDATA );
+ }
+ dtexdata_t *pTexData = GetTexData( nOutput );
+ numtexdata++;
+
+ // Save the name of the material.
+ pTexData->nameStringTableID = TexDataStringTable_AddOrFindString( pName );
+
+ // Get the width, height, view_width, view_height, and reflectivity from the material system.
+ bool bFound;
+ MaterialSystemMaterial_t matID = FindOriginalMaterial( pName, &bFound );
+ if ( matID == MATERIAL_NOT_FOUND || (!bFound) )
+ {
+ qprintf( "WARNING: material not found: \"%s\"\n", pName );
+ return nOutput;
+ }
+
+ GetMaterialDimensions( matID, &pTexData->width, &pTexData->height );
+ pTexData->view_width = pTexData->width; // undone: what is this?
+ pTexData->view_height = pTexData->height; // undone: what is this?
+
+ GetMaterialReflectivity( matID, pTexData->reflectivity.Base() );
+ g_SurfaceProperties[nOutput] = GetSurfaceProperties( matID, pName );
+
+#if 0
+ Msg( "reflectivity: %f %f %f\n",
+ pTexData->reflectivity[0],
+ pTexData->reflectivity[1],
+ pTexData->reflectivity[2] );
+#endif
+
+ return nOutput;
+}
+
+int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName )
+{
+ int existingIndex = pExistingTexData - GetTexData( 0 );
+ dtexdata_t *pNewTexData = GetTexData( numtexdata );
+ int newIndex = numtexdata;
+ numtexdata++;
+
+ *pNewTexData = *pExistingTexData;
+ pNewTexData->nameStringTableID = TexDataStringTable_AddOrFindString( cloneTexDataName );
+ g_SurfaceProperties[newIndex] = g_SurfaceProperties[existingIndex];
+
+ return newIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a texinfo that exactly matches the passed in texinfo
+//-----------------------------------------------------------------------------
+int FindTexInfo( const texinfo_t &searchTexInfo )
+{
+ for( int i = 0; i < texinfo.Count(); i++ )
+ {
+ // Just an early-out for performance
+ if ( texinfo[i].texdata != searchTexInfo.texdata )
+ continue;
+
+ if ( !memcmp( &texinfo[i], &searchTexInfo, sizeof( texinfo_t ) ) )
+ return i;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds or creates a texinfo that exactly matches the passed in texinfo
+//-----------------------------------------------------------------------------
+int FindOrCreateTexInfo( const texinfo_t &searchTexInfo )
+{
+ int i = FindTexInfo( searchTexInfo );
+ if ( i >= 0 )
+ return i;
+
+ i = texinfo.AddToTail( searchTexInfo );
+
+ if ( onlyents )
+ {
+ Error( "FindOrCreateTexInfo: Tried to create new texinfo during -onlyents compile!\nMust compile without -onlyents" );
+ }
+
+ return i;
+}
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin)
+{
+ Vector vecs[2];
+ int sv, tv;
+ vec_t ang, sinv, cosv;
+ vec_t ns, nt;
+ texinfo_t tx;
+ int i, j;
+
+ if (!bt->name[0])
+ return 0;
+
+ memset (&tx, 0, sizeof(tx));
+
+ // HLTOOLS - add support for texture vectors stored in the map file
+ if (g_nMapFileVersion < 220)
+ {
+ TextureAxisFromPlane(plane, vecs[0], vecs[1]);
+ }
+
+ if (!bt->textureWorldUnitsPerTexel[0])
+ bt->textureWorldUnitsPerTexel[0] = 1;
+ if (!bt->textureWorldUnitsPerTexel[1])
+ bt->textureWorldUnitsPerTexel[1] = 1;
+
+
+ float shiftScaleU = 1.0f / 16.0f;
+ float shiftScaleV = 1.0f / 16.0f;
+
+ if (g_nMapFileVersion < 220)
+ {
+ // rotate axis
+ if (bt->rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (bt->rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (bt->rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (bt->rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = bt->rotate / 180 * M_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (vecs[0][0])
+ sv = 0;
+ else if (vecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (vecs[1][0])
+ tv = 0;
+ else if (vecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+ nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
+ vecs[i][sv] = ns;
+ vecs[i][tv] = nt;
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ tx.textureVecsTexelsPerWorldUnits[i][j] = vecs[i][j] / bt->textureWorldUnitsPerTexel[i];
+ tx.lightmapVecsLuxelsPerWorldUnits[i][j] = tx.textureVecsTexelsPerWorldUnits[i][j] / 16.0f;
+ }
+ }
+ }
+ else
+ {
+ tx.textureVecsTexelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->textureWorldUnitsPerTexel[0];
+ tx.textureVecsTexelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->textureWorldUnitsPerTexel[0];
+ tx.textureVecsTexelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->textureWorldUnitsPerTexel[0];
+
+ tx.textureVecsTexelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->textureWorldUnitsPerTexel[1];
+ tx.textureVecsTexelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->textureWorldUnitsPerTexel[1];
+ tx.textureVecsTexelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->textureWorldUnitsPerTexel[1];
+
+ tx.lightmapVecsLuxelsPerWorldUnits[0][0] = bt->UAxis[0] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[0][1] = bt->UAxis[1] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[0][2] = bt->UAxis[2] / bt->lightmapWorldUnitsPerLuxel;
+
+ tx.lightmapVecsLuxelsPerWorldUnits[1][0] = bt->VAxis[0] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[1][1] = bt->VAxis[1] / bt->lightmapWorldUnitsPerLuxel;
+ tx.lightmapVecsLuxelsPerWorldUnits[1][2] = bt->VAxis[2] / bt->lightmapWorldUnitsPerLuxel;
+
+ shiftScaleU = bt->textureWorldUnitsPerTexel[0] / bt->lightmapWorldUnitsPerLuxel;
+ shiftScaleV = bt->textureWorldUnitsPerTexel[1] / bt->lightmapWorldUnitsPerLuxel;
+ }
+
+ tx.textureVecsTexelsPerWorldUnits[0][3] = bt->shift[0] +
+ DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[0] );
+ tx.textureVecsTexelsPerWorldUnits[1][3] = bt->shift[1] +
+ DOT_PRODUCT( origin, tx.textureVecsTexelsPerWorldUnits[1] );
+
+ tx.lightmapVecsLuxelsPerWorldUnits[0][3] = shiftScaleU * bt->shift[0] +
+ DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[0] );
+ tx.lightmapVecsLuxelsPerWorldUnits[1][3] = shiftScaleV * bt->shift[1] +
+ DOT_PRODUCT( origin, tx.lightmapVecsLuxelsPerWorldUnits[1] );
+
+ tx.flags = bt->flags;
+ tx.texdata = FindOrCreateTexData( bt->name );
+
+ // find the texinfo
+ return FindOrCreateTexInfo( tx );
+}
+
+
+void LoadSurfacePropFile( const char *pMaterialFilename )
+{
+ FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb" );
+
+ if ( fp == FILESYSTEM_INVALID_HANDLE )
+ {
+ return;
+ }
+
+ int len = g_pFileSystem->Size( fp );
+
+ char *pText = new char[len];
+ g_pFileSystem->Read( pText, len, fp );
+ g_pFileSystem->Close( fp );
+
+ physprops->ParseSurfaceData( pMaterialFilename, pText );
+
+ delete[] pText;
+}
+//-----------------------------------------------------------------------------
+// Purpose: Loads the surface properties database into the physics DLL
+//-----------------------------------------------------------------------------
+void LoadSurfaceProperties( void )
+{
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( !physicsFactory )
+ return;
+
+ physprops = (IPhysicsSurfaceProps *)physicsFactory( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION, NULL );
+
+ const char *SURFACEPROP_MANIFEST_FILE = "scripts/surfaceproperties_manifest.txt";
+ KeyValues *manifest = new KeyValues( SURFACEPROP_MANIFEST_FILE );
+ if ( manifest->LoadFromFile( g_pFileSystem, SURFACEPROP_MANIFEST_FILE, "GAME" ) )
+ {
+ for ( KeyValues *sub = manifest->GetFirstSubKey(); sub != NULL; sub = sub->GetNextKey() )
+ {
+ if ( !Q_stricmp( sub->GetName(), "file" ) )
+ {
+ // Add
+ LoadSurfacePropFile( sub->GetString() );
+ continue;
+ }
+ }
+ }
+
+ manifest->deleteThis();
+}
+
+
diff --git a/mp/src/utils/vbsp/tree.cpp b/mp/src/utils/vbsp/tree.cpp new file mode 100644 index 00000000..0f72844c --- /dev/null +++ b/mp/src/utils/vbsp/tree.cpp @@ -0,0 +1,207 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "vbsp.h"
+
+extern int c_nodes;
+
+void RemovePortalFromNode (portal_t *portal, node_t *l);
+
+node_t *NodeForPoint (node_t *node, Vector& origin)
+{
+ plane_t *plane;
+ vec_t d;
+
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &g_MainMap->mapplanes[node->planenum];
+ d = DotProduct (origin, plane->normal) - plane->dist;
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return node;
+}
+
+
+
+/*
+=============
+FreeTreePortals_r
+=============
+*/
+void FreeTreePortals_r (node_t *node)
+{
+ portal_t *p, *nextp;
+ int s;
+
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTreePortals_r (node->children[0]);
+ FreeTreePortals_r (node->children[1]);
+ }
+
+ // free portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ s = (p->nodes[1] == node);
+ nextp = p->next[s];
+
+ RemovePortalFromNode (p, p->nodes[!s]);
+ FreePortal (p);
+ }
+ node->portals = NULL;
+}
+
+/*
+=============
+FreeTree_r
+=============
+*/
+void FreeTree_r (node_t *node)
+{
+ face_t *f, *nextf;
+
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTree_r (node->children[0]);
+ FreeTree_r (node->children[1]);
+ }
+
+ // free bspbrushes
+ FreeBrushList (node->brushlist);
+
+ // free faces
+ for (f=node->faces ; f ; f=nextf)
+ {
+ nextf = f->next;
+ FreeFace (f);
+ }
+
+ // free the node
+ if (node->volume)
+ FreeBrush (node->volume);
+
+ if (numthreads == 1)
+ c_nodes--;
+ free (node);
+}
+
+
+/*
+=============
+FreeTree
+=============
+*/
+void FreeTree (tree_t *tree)
+{
+ if ( !tree )
+ return;
+
+ FreeTreePortals_r (tree->headnode);
+ FreeTree_r (tree->headnode);
+ free (tree);
+}
+
+//===============================================================
+
+void PrintTree_r (node_t *node, int depth)
+{
+ int i;
+ plane_t *plane;
+ bspbrush_t *bb;
+
+ for (i=0 ; i<depth ; i++)
+ Msg (" ");
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ if (!node->brushlist)
+ Msg ("NULL\n");
+ else
+ {
+ for (bb=node->brushlist ; bb ; bb=bb->next)
+ Msg ("%i ", bb->original->brushnum);
+ Msg ("\n");
+ }
+ return;
+ }
+
+ plane = &g_MainMap->mapplanes[node->planenum];
+ Msg ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
+ plane->normal[0], plane->normal[1], plane->normal[2],
+ plane->dist);
+ PrintTree_r (node->children[0], depth+1);
+ PrintTree_r (node->children[1], depth+1);
+}
+
+/*
+=========================================================
+
+NODES THAT DON'T SEPERATE DIFFERENT CONTENTS CAN BE PRUNED
+
+=========================================================
+*/
+
+int c_pruned;
+
+/*
+============
+PruneNodes_r
+============
+*/
+void PruneNodes_r (node_t *node)
+{
+ bspbrush_t *b, *next;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+ PruneNodes_r (node->children[0]);
+ PruneNodes_r (node->children[1]);
+
+ if ( (node->children[0]->contents & CONTENTS_SOLID)
+ && (node->children[1]->contents & CONTENTS_SOLID) )
+ {
+ if (node->faces)
+ Error ("node->faces seperating CONTENTS_SOLID");
+ if (node->children[0]->faces || node->children[1]->faces)
+ Error ("!node->faces with children");
+
+ // FIXME: free stuff
+ node->planenum = PLANENUM_LEAF;
+ node->contents = CONTENTS_SOLID;
+
+ if (node->brushlist)
+ Error ("PruneNodes: node->brushlist");
+
+ // combine brush lists
+ node->brushlist = node->children[1]->brushlist;
+
+ for (b=node->children[0]->brushlist ; b ; b=next)
+ {
+ next = b->next;
+ b->next = node->brushlist;
+ node->brushlist = b;
+ }
+
+ c_pruned++;
+ }
+}
+
+
+void PruneNodes (node_t *node)
+{
+ qprintf ("--- PruneNodes ---\n");
+ c_pruned = 0;
+ PruneNodes_r (node);
+ qprintf ("%5i pruned nodes\n", c_pruned);
+}
+
+//===========================================================
diff --git a/mp/src/utils/vbsp/vbsp-2010.vcxproj b/mp/src/utils/vbsp/vbsp-2010.vcxproj new file mode 100644 index 00000000..9719c104 --- /dev/null +++ b/mp/src/utils/vbsp/vbsp-2010.vcxproj @@ -0,0 +1,371 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vbsp</ProjectName>
+ <ProjectGuid>{E4F39B89-9731-571D-B69D-C1B8FE56C056}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vbsp</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vbsp</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vbsp.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MACRO_MATHLIB;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vbsp;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);ws2_32.lib;odbc32.lib;odbccp32.lib;winmm.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vbsp.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vbsp.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vbsp.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MACRO_MATHLIB;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vbsp;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);ws2_32.lib;odbc32.lib;odbccp32.lib;winmm.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vbsp.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vbsp.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib" />
+ <Library Include="..\..\lib\public\fgdlib.lib" />
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ <Library Include="..\..\lib\public\vtf.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="boundbox.h" />
+ <ClInclude Include="csg.h" />
+ <ClInclude Include="detail.h" />
+ <ClInclude Include="..\..\public\disp_powerinfo.h" />
+ <ClInclude Include="disp_vbsp.h" />
+ <ClInclude Include="..\..\public\disp_vertindex.h" />
+ <ClInclude Include="faces.h" />
+ <ClInclude Include="manifest.h" />
+ <ClInclude Include="map.h" />
+ <ClInclude Include="materialpatch.h" />
+ <ClInclude Include="materialsub.h" />
+ <ClInclude Include="..\common\scratchpad_helpers.h" />
+ <ClInclude Include="vbsp.h" />
+ <ClInclude Include="worldvertextransitionfixup.h" />
+ <ClInclude Include="writebsp.h" />
+ <ClInclude Include="..\common\bsplib.h" />
+ <ClInclude Include="..\..\public\builddisp.h" />
+ <ClInclude Include="..\..\public\ChunkFile.h" />
+ <ClInclude Include="..\common\cmdlib.h" />
+ <ClInclude Include="disp_ivp.h" />
+ <ClInclude Include="..\..\public\filesystem.h" />
+ <ClInclude Include="..\..\public\filesystem_helpers.h" />
+ <ClInclude Include="..\common\FileSystem_Tools.h" />
+ <ClInclude Include="..\..\public\GameBSPFile.h" />
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="ivp.h" />
+ <ClInclude Include="..\common\map_shared.h" />
+ <ClInclude Include="..\common\pacifier.h" />
+ <ClInclude Include="..\common\polylib.h" />
+ <ClInclude Include="..\..\public\tier1\tokenreader.h" />
+ <ClInclude Include="..\common\utilmatlib.h" />
+ <ClInclude Include="..\vmpi\vmpi.h" />
+ <ClInclude Include="..\..\public\zip_uncompressed.h" />
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h" />
+ <ClInclude Include="..\..\public\arraystack.h" />
+ <ClInclude Include="..\..\public\tier0\basetypes.h" />
+ <ClInclude Include="..\..\public\BSPFILE.H" />
+ <ClInclude Include="..\..\public\bspflags.h" />
+ <ClInclude Include="..\..\public\BSPTreeData.h" />
+ <ClInclude Include="..\..\public\mathlib\bumpvects.h" />
+ <ClInclude Include="..\..\public\tier1\byteswap.h" />
+ <ClInclude Include="..\..\public\cmodel.h" />
+ <ClInclude Include="..\..\public\CollisionUtils.h" />
+ <ClInclude Include="..\..\public\tier0\commonmacros.h" />
+ <ClInclude Include="..\..\public\tier0\dbg.h" />
+ <ClInclude Include="..\..\public\disp_common.h" />
+ <ClInclude Include="..\..\public\IScratchPad3D.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ <ClInclude Include="..\common\mstristrip.h" />
+ <ClInclude Include="..\..\public\nmatrix.h" />
+ <ClInclude Include="..\..\public\NTree.h" />
+ <ClInclude Include="..\..\public\nvector.h" />
+ <ClInclude Include="..\..\public\phyfile.h" />
+ <ClInclude Include="..\common\physdll.h" />
+ <ClInclude Include="..\common\qfiles.h" />
+ <ClInclude Include="..\..\public\ScratchPad3D.h" />
+ <ClInclude Include="..\common\scriplib.h" />
+ <ClInclude Include="..\..\public\studio.h" />
+ <ClInclude Include="..\common\threads.h" />
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h" />
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h" />
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h" />
+ <ClInclude Include="..\..\public\tier1\utlvector.h" />
+ <ClInclude Include="..\..\public\vcollide.h" />
+ <ClInclude Include="..\..\public\mathlib\vector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector2d.h" />
+ <ClInclude Include="..\..\public\mathlib\vector4d.h" />
+ <ClInclude Include="..\..\public\mathlib\vmatrix.h" />
+ <ClInclude Include="..\..\public\vphysics_interface.h" />
+ <ClInclude Include="..\..\public\mathlib\vplane.h" />
+ <ClInclude Include="..\..\public\wadtypes.h" />
+ <ClInclude Include="..\..\public\worldsize.h" />
+ <ClInclude Include="..\common\tools_minidump.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="boundbox.cpp" />
+ <ClCompile Include="brushbsp.cpp" />
+ <ClCompile Include="..\..\public\CollisionUtils.cpp" />
+ <ClCompile Include="csg.cpp" />
+ <ClCompile Include="cubemap.cpp" />
+ <ClCompile Include="detail.cpp" />
+ <ClCompile Include="detailObjects.cpp" />
+ <ClCompile Include="..\..\public\disp_common.cpp" />
+ <ClCompile Include="disp_ivp.cpp" />
+ <ClCompile Include="..\..\public\disp_powerinfo.cpp" />
+ <ClCompile Include="disp_vbsp.cpp" />
+ <ClCompile Include="faces.cpp" />
+ <ClCompile Include="glfile.cpp" />
+ <ClCompile Include="ivp.cpp" />
+ <ClCompile Include="leakfile.cpp" />
+ <ClCompile Include="..\..\public\loadcmdline.cpp" />
+ <ClCompile Include="..\..\public\lumpfiles.cpp" />
+ <ClCompile Include="manifest.cpp" />
+ <ClCompile Include="map.cpp" />
+ <ClCompile Include="materialpatch.cpp" />
+ <ClCompile Include="materialsub.cpp" />
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\common\mstristrip.cpp" />
+ <ClCompile Include="nodraw.cpp" />
+ <ClCompile Include="normals.cpp" />
+ <ClCompile Include="overlay.cpp" />
+ <ClCompile Include="..\common\physdll.cpp" />
+ <ClCompile Include="portals.cpp" />
+ <ClCompile Include="prtfile.cpp" />
+ <ClCompile Include="..\..\public\ScratchPad3D.cpp" />
+ <ClCompile Include="..\common\scratchpad_helpers.cpp" />
+ <ClCompile Include="StaticProp.cpp" />
+ <ClCompile Include="textures.cpp" />
+ <ClCompile Include="tree.cpp" />
+ <ClCompile Include="..\common\utilmatlib.cpp" />
+ <ClCompile Include="vbsp.cpp" />
+ <ClCompile Include="worldvertextransitionfixup.cpp" />
+ <ClCompile Include="writebsp.cpp" />
+ <ClCompile Include="..\..\public\zip_utils.cpp" />
+ <ClCompile Include="..\common\bsplib.cpp" />
+ <ClCompile Include="..\..\public\builddisp.cpp" />
+ <ClCompile Include="..\..\public\ChunkFile.cpp" />
+ <ClCompile Include="..\common\cmdlib.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="..\..\public\filesystem_init.cpp" />
+ <ClCompile Include="..\common\filesystem_tools.cpp" />
+ <ClCompile Include="..\common\map_shared.cpp" />
+ <ClCompile Include="..\common\pacifier.cpp" />
+ <ClCompile Include="..\common\polylib.cpp" />
+ <ClCompile Include="..\common\scriplib.cpp" />
+ <ClCompile Include="..\common\threads.cpp" />
+ <ClCompile Include="..\common\tools_minidump.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="notes.txt" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vbsp/vbsp-2010.vcxproj.filters b/mp/src/utils/vbsp/vbsp-2010.vcxproj.filters new file mode 100644 index 00000000..64aedaa4 --- /dev/null +++ b/mp/src/utils/vbsp/vbsp-2010.vcxproj.filters @@ -0,0 +1,446 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Common header files">
+ <UniqueIdentifier>{34125DCB-B916-13A4-10E6-A29CCCB0DD70}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Public Headers">
+ <UniqueIdentifier>{70104EB0-EB8F-8C16-99FB-ED7579D3A29D}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Common Files">
+ <UniqueIdentifier>{A7DC6913-C602-1488-0EDF-DE69D12F2421}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\fgdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vtf.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="boundbox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="csg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="detail.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\disp_powerinfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="disp_vbsp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\disp_vertindex.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="faces.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="manifest.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="map.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="materialpatch.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="materialsub.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\scratchpad_helpers.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vbsp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="worldvertextransitionfixup.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="writebsp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\bsplib.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\builddisp.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ChunkFile.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\cmdlib.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="disp_ivp.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem_helpers.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\FileSystem_Tools.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\GameBSPFile.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="ivp.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\map_shared.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\pacifier.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\polylib.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\tokenreader.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\utilmatlib.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\vmpi.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\zip_uncompressed.h">
+ <Filter>Header Files\Common header files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\arraystack.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\basetypes.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\BSPFILE.H">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\bspflags.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\BSPTreeData.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\bumpvects.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\byteswap.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\cmodel.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\CollisionUtils.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\commonmacros.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\dbg.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\disp_common.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\IScratchPad3D.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\mstristrip.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\nmatrix.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\NTree.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\nvector.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\phyfile.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\physdll.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\qfiles.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ScratchPad3D.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\scriplib.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\studio.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\threads.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlvector.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vcollide.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector2d.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector4d.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vmatrix.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vphysics_interface.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vplane.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\wadtypes.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\worldsize.h">
+ <Filter>Public Headers</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\tools_minidump.h">
+ <Filter>Source Files\Common Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="boundbox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="brushbsp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\CollisionUtils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="csg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="cubemap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="detail.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="detailObjects.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\disp_common.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="disp_ivp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\disp_powerinfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="disp_vbsp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="faces.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="glfile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ivp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="leakfile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\loadcmdline.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\lumpfiles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="manifest.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="map.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="materialpatch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="materialsub.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\mstristrip.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="nodraw.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="normals.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="overlay.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\physdll.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="portals.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prtfile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\ScratchPad3D.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\scratchpad_helpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StaticProp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="textures.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="tree.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\utilmatlib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vbsp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="worldvertextransitionfixup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="writebsp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\zip_utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\bsplib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\builddisp.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\ChunkFile.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\cmdlib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_init.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\filesystem_tools.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\map_shared.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\pacifier.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\polylib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\scriplib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\threads.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\tools_minidump.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="notes.txt">
+ <Filter></Filter>
+ </None>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vbsp/vbsp.cpp b/mp/src/utils/vbsp/vbsp.cpp new file mode 100644 index 00000000..1c7b7ec9 --- /dev/null +++ b/mp/src/utils/vbsp/vbsp.cpp @@ -0,0 +1,1404 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: BSP Building tool
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vbsp.h"
+#include "detail.h"
+#include "physdll.h"
+#include "utilmatlib.h"
+#include "disp_vbsp.h"
+#include "writebsp.h"
+#include "tier0/icommandline.h"
+#include "materialsystem/imaterialsystem.h"
+#include "map.h"
+#include "tools_minidump.h"
+#include "materialsub.h"
+#include "loadcmdline.h"
+#include "byteswap.h"
+#include "worldvertextransitionfixup.h"
+
+extern float g_maxLightmapDimension;
+
+char source[1024];
+char mapbase[ 64 ];
+char name[1024];
+char materialPath[1024];
+
+vec_t microvolume = 1.0;
+qboolean noprune;
+qboolean glview;
+qboolean nodetail;
+qboolean fulldetail;
+qboolean onlyents;
+bool onlyprops;
+qboolean nomerge;
+qboolean nomergewater = false;
+qboolean nowater;
+qboolean nocsg;
+qboolean noweld;
+qboolean noshare;
+qboolean nosubdiv;
+qboolean notjunc;
+qboolean noopt;
+qboolean leaktest;
+qboolean verboseentities;
+qboolean dumpcollide = false;
+qboolean g_bLowPriority = false;
+qboolean g_DumpStaticProps = false;
+qboolean g_bSkyVis = false; // skybox vis is off by default, toggle this to enable it
+bool g_bLightIfMissing = false;
+bool g_snapAxialPlanes = false;
+bool g_bKeepStaleZip = false;
+bool g_NodrawTriggers = false;
+bool g_DisableWaterLighting = false;
+bool g_bAllowDetailCracks = false;
+bool g_bNoVirtualMesh = false;
+
+float g_defaultLuxelSize = DEFAULT_LUXEL_SIZE;
+float g_luxelScale = 1.0f;
+float g_minLuxelScale = 1.0f;
+bool g_BumpAll = false;
+
+int g_nDXLevel = 0; // default dxlevel if you don't specify it on the command-line.
+CUtlVector<int> g_SkyAreas;
+char outbase[32];
+
+// HLTOOLS: Introduce these calcs to make the block algorithm proportional to the proper
+// world coordinate extents. Assumes square spatial constraints.
+#define BLOCKS_SIZE 1024
+#define BLOCKS_SPACE (COORD_EXTENT/BLOCKS_SIZE)
+#define BLOCKX_OFFSET ((BLOCKS_SPACE/2)+1)
+#define BLOCKY_OFFSET ((BLOCKS_SPACE/2)+1)
+#define BLOCKS_MIN (-(BLOCKS_SPACE/2))
+#define BLOCKS_MAX ((BLOCKS_SPACE/2)-1)
+
+int block_xl = BLOCKS_MIN, block_xh = BLOCKS_MAX, block_yl = BLOCKS_MIN, block_yh = BLOCKS_MAX;
+
+int entity_num;
+
+
+node_t *block_nodes[BLOCKS_SPACE+2][BLOCKS_SPACE+2];
+
+//-----------------------------------------------------------------------------
+// Assign occluder areas (must happen *after* the world model is processed)
+//-----------------------------------------------------------------------------
+void AssignOccluderAreas( tree_t *pTree );
+static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas );
+
+
+/*
+============
+BlockTree
+
+============
+*/
+node_t *BlockTree (int xl, int yl, int xh, int yh)
+{
+ node_t *node;
+ Vector normal;
+ float dist;
+ int mid;
+
+ if (xl == xh && yl == yh)
+ {
+ node = block_nodes[xl+BLOCKX_OFFSET][yl+BLOCKY_OFFSET];
+ if (!node)
+ { // return an empty leaf
+ node = AllocNode ();
+ node->planenum = PLANENUM_LEAF;
+ node->contents = 0; //CONTENTS_SOLID;
+ return node;
+ }
+ return node;
+ }
+
+ // create a seperator along the largest axis
+ node = AllocNode ();
+
+ if (xh - xl > yh - yl)
+ { // split x axis
+ mid = xl + (xh-xl)/2 + 1;
+ normal[0] = 1;
+ normal[1] = 0;
+ normal[2] = 0;
+ dist = mid*BLOCKS_SIZE;
+ node->planenum = g_MainMap->FindFloatPlane (normal, dist);
+ node->children[0] = BlockTree ( mid, yl, xh, yh);
+ node->children[1] = BlockTree ( xl, yl, mid-1, yh);
+ }
+ else
+ {
+ mid = yl + (yh-yl)/2 + 1;
+ normal[0] = 0;
+ normal[1] = 1;
+ normal[2] = 0;
+ dist = mid*BLOCKS_SIZE;
+ node->planenum = g_MainMap->FindFloatPlane (normal, dist);
+ node->children[0] = BlockTree ( xl, mid, xh, yh);
+ node->children[1] = BlockTree ( xl, yl, xh, mid-1);
+ }
+
+ return node;
+}
+
+/*
+============
+ProcessBlock_Thread
+
+============
+*/
+int brush_start, brush_end;
+void ProcessBlock_Thread (int threadnum, int blocknum)
+{
+ int xblock, yblock;
+ Vector mins, maxs;
+ bspbrush_t *brushes;
+ tree_t *tree;
+ node_t *node;
+
+ yblock = block_yl + blocknum / (block_xh-block_xl+1);
+ xblock = block_xl + blocknum % (block_xh-block_xl+1);
+
+ qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
+
+ mins[0] = xblock*BLOCKS_SIZE;
+ mins[1] = yblock*BLOCKS_SIZE;
+ mins[2] = MIN_COORD_INTEGER;
+ maxs[0] = (xblock+1)*BLOCKS_SIZE;
+ maxs[1] = (yblock+1)*BLOCKS_SIZE;
+ maxs[2] = MAX_COORD_INTEGER;
+
+ // the makelist and chopbrushes could be cached between the passes...
+ brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs, NO_DETAIL);
+ if (!brushes)
+ {
+ node = AllocNode ();
+ node->planenum = PLANENUM_LEAF;
+ node->contents = CONTENTS_SOLID;
+ block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = node;
+ return;
+ }
+
+ FixupAreaportalWaterBrushes( brushes );
+ if (!nocsg)
+ brushes = ChopBrushes (brushes);
+
+ tree = BrushBSP (brushes, mins, maxs);
+
+ block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = tree->headnode;
+}
+
+
+/*
+============
+ProcessWorldModel
+
+============
+*/
+void SplitSubdividedFaces( node_t *headnode ); // garymcthack
+void ProcessWorldModel (void)
+{
+ entity_t *e;
+ tree_t *tree = NULL;
+ qboolean leaked;
+ int optimize;
+ int start;
+
+ e = &entities[entity_num];
+
+ brush_start = e->firstbrush;
+ brush_end = brush_start + e->numbrushes;
+ leaked = false;
+
+ //
+ // perform per-block operations
+ //
+ if (block_xh * BLOCKS_SIZE > g_MainMap->map_maxs[0])
+ {
+ block_xh = floor(g_MainMap->map_maxs[0]/BLOCKS_SIZE);
+ }
+ if ( (block_xl+1) * BLOCKS_SIZE < g_MainMap->map_mins[0])
+ {
+ block_xl = floor(g_MainMap->map_mins[0]/BLOCKS_SIZE);
+ }
+ if (block_yh * BLOCKS_SIZE > g_MainMap->map_maxs[1])
+ {
+ block_yh = floor(g_MainMap->map_maxs[1]/BLOCKS_SIZE);
+ }
+ if ( (block_yl+1) * BLOCKS_SIZE < g_MainMap->map_mins[1])
+ {
+ block_yl = floor(g_MainMap->map_mins[1]/BLOCKS_SIZE);
+ }
+
+ // HLTOOLS: updated to +/- MAX_COORD_INTEGER ( new world size limits / worldsize.h )
+ if (block_xl < BLOCKS_MIN)
+ {
+ block_xl = BLOCKS_MIN;
+ }
+ if (block_yl < BLOCKS_MIN)
+ {
+ block_yl = BLOCKS_MIN;
+ }
+ if (block_xh > BLOCKS_MAX)
+ {
+ block_xh = BLOCKS_MAX;
+ }
+ if (block_yh > BLOCKS_MAX)
+ {
+ block_yh = BLOCKS_MAX;
+ }
+
+ for (optimize = 0 ; optimize <= 1 ; optimize++)
+ {
+ qprintf ("--------------------------------------------\n");
+
+ RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
+ !verbose, ProcessBlock_Thread);
+
+ //
+ // build the division tree
+ // oversizing the blocks guarantees that all the boundaries
+ // will also get nodes.
+ //
+
+ qprintf ("--------------------------------------------\n");
+
+ tree = AllocTree ();
+ tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
+
+ tree->mins[0] = (block_xl)*BLOCKS_SIZE;
+ tree->mins[1] = (block_yl)*BLOCKS_SIZE;
+ tree->mins[2] = g_MainMap->map_mins[2] - 8;
+
+ tree->maxs[0] = (block_xh+1)*BLOCKS_SIZE;
+ tree->maxs[1] = (block_yh+1)*BLOCKS_SIZE;
+ tree->maxs[2] = g_MainMap->map_maxs[2] + 8;
+
+ //
+ // perform the global operations
+ //
+
+ // make the portals/faces by traversing down to each empty leaf
+ MakeTreePortals (tree);
+
+ if (FloodEntities (tree))
+ {
+ // turns everthing outside into solid
+ FillOutside (tree->headnode);
+ }
+ else
+ {
+ Warning( ("**** leaked ****\n") );
+ leaked = true;
+ LeakFile (tree);
+ if (leaktest)
+ {
+ Warning( ("--- MAP LEAKED ---\n") );
+ exit (0);
+ }
+ }
+
+ // mark the brush sides that actually turned into faces
+ MarkVisibleSides (tree, brush_start, brush_end, NO_DETAIL);
+ if (noopt || leaked)
+ break;
+ if (!optimize)
+ {
+ // If we are optimizing, free the tree. Next time we will construct it again, but
+ // we'll use the information in MarkVisibleSides() so we'll only split with planes that
+ // actually contribute renderable geometry
+ FreeTree (tree);
+ }
+ }
+
+ FloodAreas (tree);
+
+ RemoveAreaPortalBrushes_R( tree->headnode );
+
+ start = Plat_FloatTime();
+ Msg("Building Faces...");
+ // this turns portals with one solid side into faces
+ // it also subdivides each face if necessary to fit max lightmap dimensions
+ MakeFaces (tree->headnode);
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+
+ if (glview)
+ {
+ WriteGLView (tree, source);
+ }
+
+ AssignOccluderAreas( tree );
+ Compute3DSkyboxAreas( tree->headnode, g_SkyAreas );
+ face_t *pLeafFaceList = NULL;
+ if ( !nodetail )
+ {
+ pLeafFaceList = MergeDetailTree( tree, brush_start, brush_end );
+ }
+
+ start = Plat_FloatTime();
+
+ Msg("FixTjuncs...\n");
+
+ // This unifies the vertex list for all edges (splits collinear edges to remove t-junctions)
+ // It also welds the list of vertices out of each winding/portal and rounds nearly integer verts to integer
+ pLeafFaceList = FixTjuncs (tree->headnode, pLeafFaceList);
+
+ // this merges all of the solid nodes that have separating planes
+ if (!noprune)
+ {
+ Msg("PruneNodes...\n");
+ PruneNodes (tree->headnode);
+ }
+
+// Msg( "SplitSubdividedFaces...\n" );
+// SplitSubdividedFaces( tree->headnode );
+
+ Msg("WriteBSP...\n");
+ WriteBSP (tree->headnode, pLeafFaceList);
+ Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
+
+ if (!leaked)
+ {
+ WritePortalFile (tree);
+ }
+
+ FreeTree( tree );
+ FreeLeafFaces( pLeafFaceList );
+}
+
+/*
+============
+ProcessSubModel
+
+============
+*/
+void ProcessSubModel( )
+{
+ entity_t *e;
+ int start, end;
+ tree_t *tree;
+ bspbrush_t *list;
+ Vector mins, maxs;
+
+ e = &entities[entity_num];
+
+ start = e->firstbrush;
+ end = start + e->numbrushes;
+
+ mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
+ maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
+ list = MakeBspBrushList (start, end, mins, maxs, FULL_DETAIL);
+
+ if (!nocsg)
+ list = ChopBrushes (list);
+ tree = BrushBSP (list, mins, maxs);
+
+ // This would wind up crashing the engine because we'd have a negative leaf index in dmodel_t::headnode.
+ if ( tree->headnode->planenum == PLANENUM_LEAF )
+ {
+ const char *pClassName = ValueForKey( e, "classname" );
+ const char *pTargetName = ValueForKey( e, "targetname" );
+ Error( "bmodel %d has no head node (class '%s', targetname '%s')", entity_num, pClassName, pTargetName );
+ }
+
+ MakeTreePortals (tree);
+
+#if DEBUG_BRUSHMODEL
+ if ( entity_num == DEBUG_BRUSHMODEL )
+ WriteGLView( tree, "tree_all" );
+#endif
+
+ MarkVisibleSides (tree, start, end, FULL_DETAIL);
+ MakeFaces (tree->headnode);
+
+ FixTjuncs( tree->headnode, NULL );
+ WriteBSP( tree->headnode, NULL );
+
+#if DEBUG_BRUSHMODEL
+ if ( entity_num == DEBUG_BRUSHMODEL )
+ {
+ WriteGLView( tree, "tree_vis" );
+ WriteGLViewFaces( tree, "tree_faces" );
+ }
+#endif
+
+ FreeTree (tree);
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the entity is a func_occluder
+//-----------------------------------------------------------------------------
+bool IsFuncOccluder( int entity_num )
+{
+ entity_t *mapent = &entities[entity_num];
+ const char *pClassName = ValueForKey( mapent, "classname" );
+ return (strcmp("func_occluder", pClassName) == 0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the area of a brush's occluders
+//-----------------------------------------------------------------------------
+float ComputeOccluderBrushArea( mapbrush_t *pBrush )
+{
+ float flArea = 0.0f;
+ for ( int j = 0; j < pBrush->numsides; ++j )
+ {
+ side_t *pSide = &(pBrush->original_sides[j]);
+
+ // Skip nodraw surfaces
+ if ( texinfo[pSide->texinfo].flags & SURF_NODRAW )
+ continue;
+
+ if ( !pSide->winding )
+ continue;
+
+ flArea += WindingArea( pSide->winding );
+ }
+
+ return flArea;
+}
+
+
+//-----------------------------------------------------------------------------
+// Clips all occluder brushes against each other
+//-----------------------------------------------------------------------------
+static tree_t *ClipOccluderBrushes( )
+{
+ // Create a list of all occluder brushes in the level
+ CUtlVector< mapbrush_t * > mapBrushes( 1024, 1024 );
+ for ( entity_num=0; entity_num < g_MainMap->num_entities; ++entity_num )
+ {
+ if (!IsFuncOccluder(entity_num))
+ continue;
+
+ entity_t *e = &entities[entity_num];
+ int end = e->firstbrush + e->numbrushes;
+ int i;
+ for ( i = e->firstbrush; i < end; ++i )
+ {
+ mapBrushes.AddToTail( &g_MainMap->mapbrushes[i] );
+ }
+ }
+
+ int nBrushCount = mapBrushes.Count();
+ if ( nBrushCount == 0 )
+ return NULL;
+
+ Vector mins, maxs;
+ mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
+ maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
+
+ bspbrush_t *list = MakeBspBrushList( mapBrushes.Base(), nBrushCount, mins, maxs );
+
+ if (!nocsg)
+ list = ChopBrushes (list);
+ tree_t *tree = BrushBSP (list, mins, maxs);
+ MakeTreePortals (tree);
+ MarkVisibleSides (tree, mapBrushes.Base(), nBrushCount);
+ MakeFaces( tree->headnode );
+
+ // NOTE: This will output the occluder face vertices + planes
+ FixTjuncs( tree->headnode, NULL );
+
+ return tree;
+}
+
+
+//-----------------------------------------------------------------------------
+// Generate a list of unique sides in the occluder tree
+//-----------------------------------------------------------------------------
+static void GenerateOccluderSideList( int nEntity, CUtlVector<side_t*> &occluderSides )
+{
+ entity_t *e = &entities[nEntity];
+ int end = e->firstbrush + e->numbrushes;
+ int i, j;
+ for ( i = e->firstbrush; i < end; ++i )
+ {
+ mapbrush_t *mb = &g_MainMap->mapbrushes[i];
+ for ( j = 0; j < mb->numsides; ++j )
+ {
+ occluderSides.AddToTail( &(mb->original_sides[j]) );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Generate a list of unique faces in the occluder tree
+//-----------------------------------------------------------------------------
+static void GenerateOccluderFaceList( node_t *pOccluderNode, CUtlVector<face_t*> &occluderFaces )
+{
+ if (pOccluderNode->planenum == PLANENUM_LEAF)
+ return;
+
+ for ( face_t *f=pOccluderNode->faces ; f ; f = f->next )
+ {
+ occluderFaces.AddToTail( f );
+ }
+
+ GenerateOccluderFaceList( pOccluderNode->children[0], occluderFaces );
+ GenerateOccluderFaceList( pOccluderNode->children[1], occluderFaces );
+}
+
+
+//-----------------------------------------------------------------------------
+// For occluder area assignment
+//-----------------------------------------------------------------------------
+struct OccluderInfo_t
+{
+ int m_nOccluderEntityIndex;
+};
+
+static CUtlVector< OccluderInfo_t > g_OccluderInfo;
+
+
+//-----------------------------------------------------------------------------
+// Emits occluder brushes
+//-----------------------------------------------------------------------------
+static void EmitOccluderBrushes()
+{
+ char str[64];
+
+ g_OccluderData.RemoveAll();
+ g_OccluderPolyData.RemoveAll();
+ g_OccluderVertexIndices.RemoveAll();
+
+ tree_t *pOccluderTree = ClipOccluderBrushes();
+ if (!pOccluderTree)
+ return;
+
+ CUtlVector<face_t*> faceList( 1024, 1024 );
+ CUtlVector<side_t*> sideList( 1024, 1024 );
+ GenerateOccluderFaceList( pOccluderTree->headnode, faceList );
+
+#ifdef _DEBUG
+ int *pEmitted = (int*)stackalloc( faceList.Count() * sizeof(int) );
+ memset( pEmitted, 0, faceList.Count() * sizeof(int) );
+#endif
+
+ for ( entity_num=1; entity_num < num_entities; ++entity_num )
+ {
+ if (!IsFuncOccluder(entity_num))
+ continue;
+
+ // Output only those parts of the occluder tree which are a part of the brush
+ int nOccluder = g_OccluderData.AddToTail();
+ doccluderdata_t &occluderData = g_OccluderData[ nOccluder ];
+ occluderData.firstpoly = g_OccluderPolyData.Count();
+ occluderData.mins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
+ occluderData.maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+ occluderData.flags = 0;
+ occluderData.area = -1;
+
+ // NOTE: If you change the algorithm by which occluder numbers are allocated,
+ // then you must also change FixupOnlyEntsOccluderEntities() below
+ sprintf (str, "%i", nOccluder);
+ SetKeyValue (&entities[entity_num], "occludernumber", str);
+
+ int nIndex = g_OccluderInfo.AddToTail();
+ g_OccluderInfo[nIndex].m_nOccluderEntityIndex = entity_num;
+
+ sideList.RemoveAll();
+ GenerateOccluderSideList( entity_num, sideList );
+ for ( int i = faceList.Count(); --i >= 0; )
+ {
+ // Skip nodraw surfaces, but not triggers that have been marked as nodraw
+ face_t *f = faceList[i];
+ if ( ( texinfo[f->texinfo].flags & SURF_NODRAW ) &&
+ (( texinfo[f->texinfo].flags & SURF_TRIGGER ) == 0 ) )
+ continue;
+
+ // Only emit faces that appear in the side list of the occluder
+ for ( int j = sideList.Count(); --j >= 0; )
+ {
+ if ( sideList[j] != f->originalface )
+ continue;
+
+ if ( f->numpoints < 3 )
+ continue;
+
+ // not a final face
+ Assert ( !f->merged && !f->split[0] && !f->split[1] );
+
+#ifdef _DEBUG
+ Assert( !pEmitted[i] );
+ pEmitted[i] = entity_num;
+#endif
+
+ int k = g_OccluderPolyData.AddToTail();
+ doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[k];
+
+ pOccluderPoly->planenum = f->planenum;
+ pOccluderPoly->vertexcount = f->numpoints;
+ pOccluderPoly->firstvertexindex = g_OccluderVertexIndices.Count();
+ for( k = 0; k < f->numpoints; ++k )
+ {
+ g_OccluderVertexIndices.AddToTail( f->vertexnums[k] );
+
+ const Vector &p = dvertexes[f->vertexnums[k]].point;
+ VectorMin( occluderData.mins, p, occluderData.mins );
+ VectorMax( occluderData.maxs, p, occluderData.maxs );
+ }
+
+ break;
+ }
+ }
+
+ occluderData.polycount = g_OccluderPolyData.Count() - occluderData.firstpoly;
+
+ // Mark this brush as not having brush geometry so it won't be re-emitted with a brush model
+ entities[entity_num].numbrushes = 0;
+ }
+
+ FreeTree( pOccluderTree );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set occluder area
+//-----------------------------------------------------------------------------
+void SetOccluderArea( int nOccluder, int nArea, int nEntityNum )
+{
+ if ( g_OccluderData[nOccluder].area <= 0 )
+ {
+ g_OccluderData[nOccluder].area = nArea;
+ }
+ else if ( (nArea != 0) && (g_OccluderData[nOccluder].area != nArea) )
+ {
+ const char *pTargetName = ValueForKey( &entities[nEntityNum], "targetname" );
+ if (!pTargetName)
+ {
+ pTargetName = "<no name>";
+ }
+ Warning("Occluder \"%s\" straddles multiple areas. This is invalid!\n", pTargetName );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Assign occluder areas (must happen *after* the world model is processed)
+//-----------------------------------------------------------------------------
+void AssignAreaToOccluder( int nOccluder, tree_t *pTree, bool bCrossAreaPortals )
+{
+ int nFirstPoly = g_OccluderData[nOccluder].firstpoly;
+ int nEntityNum = g_OccluderInfo[nOccluder].m_nOccluderEntityIndex;
+ for ( int j = 0; j < g_OccluderData[nOccluder].polycount; ++j )
+ {
+ doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[nFirstPoly + j];
+ int nFirstVertex = pOccluderPoly->firstvertexindex;
+ for ( int k = 0; k < pOccluderPoly->vertexcount; ++k )
+ {
+ int nVertexIndex = g_OccluderVertexIndices[nFirstVertex + k];
+ node_t *pNode = NodeForPoint( pTree->headnode, dvertexes[ nVertexIndex ].point );
+
+ SetOccluderArea( nOccluder, pNode->area, nEntityNum );
+
+ int nOtherSideIndex;
+ portal_t *pPortal;
+ for ( pPortal = pNode->portals; pPortal; pPortal = pPortal->next[!nOtherSideIndex] )
+ {
+ nOtherSideIndex = (pPortal->nodes[0] == pNode) ? 1 : 0;
+ if (!pPortal->onnode)
+ continue; // edge of world
+
+ // Don't cross over area portals for the area check
+ if ((!bCrossAreaPortals) && pPortal->nodes[nOtherSideIndex]->contents & CONTENTS_AREAPORTAL)
+ continue;
+
+ int nAdjacentArea = pPortal->nodes[nOtherSideIndex] ? pPortal->nodes[nOtherSideIndex]->area : 0;
+ SetOccluderArea( nOccluder, nAdjacentArea, nEntityNum );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Assign occluder areas (must happen *after* the world model is processed)
+//-----------------------------------------------------------------------------
+void AssignOccluderAreas( tree_t *pTree )
+{
+ for ( int i = 0; i < g_OccluderData.Count(); ++i )
+ {
+ AssignAreaToOccluder( i, pTree, false );
+
+ // This can only have happened if the only valid portal out leads into an areaportal
+ if ( g_OccluderData[i].area <= 0 )
+ {
+ AssignAreaToOccluder( i, pTree, true );
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Make sure the func_occluders have the appropriate data set
+//-----------------------------------------------------------------------------
+void FixupOnlyEntsOccluderEntities()
+{
+ char str[64];
+ int nOccluder = 0;
+ for ( entity_num=1; entity_num < num_entities; ++entity_num )
+ {
+ if (!IsFuncOccluder(entity_num))
+ continue;
+
+ // NOTE: If you change the algorithm by which occluder numbers are allocated above,
+ // then you must also change this
+ sprintf (str, "%i", nOccluder);
+ SetKeyValue (&entities[entity_num], "occludernumber", str);
+ ++nOccluder;
+ }
+}
+
+
+void MarkNoDynamicShadowSides()
+{
+ for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
+ {
+ g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = true;
+ }
+
+ for ( int i=0; i < g_NoDynamicShadowSides.Count(); i++ )
+ {
+ int brushSideID = g_NoDynamicShadowSides[i];
+
+ // Find the side with this ID.
+ for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
+ {
+ if ( g_MainMap->brushsides[iSide].id == brushSideID )
+ g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = false;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compute the 3D skybox areas
+//-----------------------------------------------------------------------------
+static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas )
+{
+ for (int i = 0; i < g_MainMap->num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "sky_camera"))
+ {
+ // Found a 3D skybox camera, get a leaf that lies in it
+ node_t *pLeaf = PointInLeaf( headnode, entities[i].origin );
+ if (pLeaf->contents & CONTENTS_SOLID)
+ {
+ Error ("Error! Entity sky_camera in solid volume! at %.1f %.1f %.1f\n", entities[i].origin.x, entities[i].origin.y, entities[i].origin.z);
+ }
+ areas.AddToTail( pLeaf->area );
+ }
+ }
+}
+
+bool Is3DSkyboxArea( int area )
+{
+ for ( int i = g_SkyAreas.Count(); --i >=0; )
+ {
+ if ( g_SkyAreas[i] == area )
+ return true;
+ }
+ return false;
+}
+
+
+/*
+============
+ProcessModels
+============
+*/
+void ProcessModels (void)
+{
+ BeginBSPFile ();
+
+ // Mark sides that have no dynamic shadows.
+ MarkNoDynamicShadowSides();
+
+ // emit the displacement surfaces
+ EmitInitialDispInfos();
+
+ // Clip occluder brushes against each other,
+ // Remove them from the list of models to process below
+ EmitOccluderBrushes( );
+
+ for ( entity_num=0; entity_num < num_entities; ++entity_num )
+ {
+ entity_t *pEntity = &entities[entity_num];
+ if ( !pEntity->numbrushes )
+ continue;
+
+ qprintf ("############### model %i ###############\n", nummodels);
+
+ BeginModel ();
+
+ if (entity_num == 0)
+ {
+ ProcessWorldModel();
+ }
+ else
+ {
+ ProcessSubModel( );
+ }
+
+ EndModel ();
+
+ if (!verboseentities)
+ {
+ verbose = false; // don't bother printing submodels
+ }
+ }
+
+ // Turn the skybox into a cubemap in case we don't build env_cubemap textures.
+ Cubemap_CreateDefaultCubemaps();
+ EndBSPFile ();
+}
+
+
+void LoadPhysicsDLL( void )
+{
+ PhysicsDLLPath( "vphysics.dll" );
+}
+
+
+void PrintCommandLine( int argc, char **argv )
+{
+ Warning( "Command line: " );
+ for ( int z=0; z < argc; z++ )
+ {
+ Warning( "\"%s\" ", argv[z] );
+ }
+ Warning( "\n\n" );
+}
+
+
+int RunVBSP( int argc, char **argv )
+{
+ int i;
+ double start, end;
+ char path[1024];
+
+ CommandLine()->CreateCmdLine( argc, argv );
+ MathLib_Init( 2.2f, 2.2f, 0.0f, OVERBRIGHT, false, false, false, false );
+ InstallSpewFunction();
+ SpewActivate( "developer", 1 );
+
+ CmdLib_InitFileSystem( argv[ argc-1 ] );
+
+ Q_StripExtension( ExpandArg( argv[ argc-1 ] ), source, sizeof( source ) );
+ Q_FileBase( source, mapbase, sizeof( mapbase ) );
+ strlwr( mapbase );
+
+ LoadCmdLineFromFile( argc, argv, mapbase, "vbsp" );
+
+ Msg( "Valve Software - vbsp.exe (%s)\n", __DATE__ );
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!stricmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!Q_stricmp(argv[i],"-glview"))
+ {
+ glview = true;
+ }
+ else if ( !Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose") )
+ {
+ Msg("verbose = true\n");
+ verbose = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noweld"))
+ {
+ Msg ("noweld = true\n");
+ noweld = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nocsg"))
+ {
+ Msg ("nocsg = true\n");
+ nocsg = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noshare"))
+ {
+ Msg ("noshare = true\n");
+ noshare = true;
+ }
+ else if (!Q_stricmp(argv[i], "-notjunc"))
+ {
+ Msg ("notjunc = true\n");
+ notjunc = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nowater"))
+ {
+ Msg ("nowater = true\n");
+ nowater = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noopt"))
+ {
+ Msg ("noopt = true\n");
+ noopt = true;
+ }
+ else if (!Q_stricmp(argv[i], "-noprune"))
+ {
+ Msg ("noprune = true\n");
+ noprune = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nomerge"))
+ {
+ Msg ("nomerge = true\n");
+ nomerge = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nomergewater"))
+ {
+ Msg ("nomergewater = true\n");
+ nomergewater = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nosubdiv"))
+ {
+ Msg ("nosubdiv = true\n");
+ nosubdiv = true;
+ }
+ else if (!Q_stricmp(argv[i], "-nodetail"))
+ {
+ Msg ("nodetail = true\n");
+ nodetail = true;
+ }
+ else if (!Q_stricmp(argv[i], "-fulldetail"))
+ {
+ Msg ("fulldetail = true\n");
+ fulldetail = true;
+ }
+ else if (!Q_stricmp(argv[i], "-onlyents"))
+ {
+ Msg ("onlyents = true\n");
+ onlyents = true;
+ }
+ else if (!Q_stricmp(argv[i], "-onlyprops"))
+ {
+ Msg ("onlyprops = true\n");
+ onlyprops = true;
+ }
+ else if (!Q_stricmp(argv[i], "-micro"))
+ {
+ microvolume = atof(argv[i+1]);
+ Msg ("microvolume = %f\n", microvolume);
+ i++;
+ }
+ else if (!Q_stricmp(argv[i], "-leaktest"))
+ {
+ Msg ("leaktest = true\n");
+ leaktest = true;
+ }
+ else if (!Q_stricmp(argv[i], "-verboseentities"))
+ {
+ Msg ("verboseentities = true\n");
+ verboseentities = true;
+ }
+ else if (!Q_stricmp(argv[i], "-snapaxial"))
+ {
+ Msg ("snap axial = true\n");
+ g_snapAxialPlanes = true;
+ }
+#if 0
+ else if (!Q_stricmp(argv[i], "-maxlightmapdim"))
+ {
+ g_maxLightmapDimension = atof(argv[i+1]);
+ Msg ("g_maxLightmapDimension = %f\n", g_maxLightmapDimension);
+ i++;
+ }
+#endif
+ else if (!Q_stricmp(argv[i], "-block"))
+ {
+ block_xl = block_xh = atoi(argv[i+1]);
+ block_yl = block_yh = atoi(argv[i+2]);
+ Msg ("block: %i,%i\n", block_xl, block_yl);
+ i+=2;
+ }
+ else if (!Q_stricmp(argv[i], "-blocks"))
+ {
+ block_xl = atoi(argv[i+1]);
+ block_yl = atoi(argv[i+2]);
+ block_xh = atoi(argv[i+3]);
+ block_yh = atoi(argv[i+4]);
+ Msg ("blocks: %i,%i to %i,%i\n",
+ block_xl, block_yl, block_xh, block_yh);
+ i+=4;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumpcollide" ) )
+ {
+ Msg("Dumping collision models to collideXXX.txt\n" );
+ dumpcollide = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumpstaticprop" ) )
+ {
+ Msg("Dumping static props to staticpropXXX.txt\n" );
+ g_DumpStaticProps = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-forceskyvis" ) )
+ {
+ Msg("Enabled vis in 3d skybox\n" );
+ g_bSkyVis = true;
+ }
+ else if (!Q_stricmp (argv[i],"-tmpout"))
+ {
+ strcpy (outbase, "/tmp");
+ }
+#if 0
+ else if( !Q_stricmp( argv[i], "-defaultluxelsize" ) )
+ {
+ g_defaultLuxelSize = atof( argv[i+1] );
+ i++;
+ }
+#endif
+ else if( !Q_stricmp( argv[i], "-luxelscale" ) )
+ {
+ g_luxelScale = atof( argv[i+1] );
+ i++;
+ }
+ else if( !strcmp( argv[i], "-minluxelscale" ) )
+ {
+ g_minLuxelScale = atof( argv[i+1] );
+ if (g_minLuxelScale < 1)
+ g_minLuxelScale = 1;
+ i++;
+ }
+ else if( !Q_stricmp( argv[i], "-dxlevel" ) )
+ {
+ g_nDXLevel = atoi( argv[i+1] );
+ Msg( "DXLevel = %d\n", g_nDXLevel );
+ i++;
+ }
+ else if( !Q_stricmp( argv[i], "-bumpall" ) )
+ {
+ g_BumpAll = true;
+ }
+ else if( !Q_stricmp( argv[i], "-low" ) )
+ {
+ g_bLowPriority = true;
+ }
+ else if( !Q_stricmp( argv[i], "-lightifmissing" ) )
+ {
+ g_bLightIfMissing = true;
+ }
+ else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
+ {
+ }
+ else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
+ {
+ // nothing to do here, but don't bail on this option
+ }
+ else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
+ {
+ ++i;
+ }
+ else if ( !Q_stricmp( argv[i], "-keepstalezip" ) )
+ {
+ g_bKeepStaleZip = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-xbox" ) )
+ {
+ // enable mandatory xbox extensions
+ g_NodrawTriggers = true;
+ g_DisableWaterLighting = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-allowdetailcracks"))
+ {
+ g_bAllowDetailCracks = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-novirtualmesh"))
+ {
+ g_bNoVirtualMesh = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-replacematerials" ) )
+ {
+ g_ReplaceMaterials = true;
+ }
+ else if ( !Q_stricmp(argv[i], "-nodrawtriggers") )
+ {
+ g_NodrawTriggers = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
+ {
+ EnableFullMinidumps( true );
+ }
+ else if (argv[i][0] == '-')
+ {
+ Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
+ i = 100000; // force it to print the usage
+ break;
+ }
+ else
+ break;
+ }
+
+ if (i != argc - 1)
+ {
+ PrintCommandLine( argc, argv );
+
+ Warning(
+ "usage : vbsp [options...] mapfile\n"
+ "example: vbsp -onlyents c:\\hl2\\hl2\\maps\\test\n"
+ "\n"
+ "Common options (use -v to see all options):\n"
+ "\n"
+ " -v (or -verbose): Turn on verbose output (also shows more command\n"
+ " line options).\n"
+ "\n"
+ " -onlyents : This option causes vbsp only import the entities from the .vmf\n"
+ " file. -onlyents won't reimport brush models.\n"
+ " -onlyprops : Only update the static props and detail props.\n"
+ " -glview : Writes .gl files in the current directory that can be viewed\n"
+ " with glview.exe. If you use -tmpout, it will write the files\n"
+ " into the \\tmp folder.\n"
+ " -nodetail : Get rid of all detail geometry. The geometry left over is\n"
+ " what affects visibility.\n"
+ " -nowater : Get rid of water brushes.\n"
+ " -low : Run as an idle-priority process.\n"
+ "\n"
+ " -vproject <directory> : Override the VPROJECT environment variable.\n"
+ " -game <directory> : Same as -vproject.\n"
+ "\n" );
+
+ if ( verbose )
+ {
+ Warning(
+ "Other options :\n"
+ " -novconfig : Don't bring up graphical UI on vproject errors.\n"
+ " -threads : Control the number of threads vbsp uses (defaults to the # of\n"
+ " processors on your machine).\n"
+ " -verboseentities: If -v is on, this disables verbose output for submodels.\n"
+ " -noweld : Don't join face vertices together.\n"
+ " -nocsg : Don't chop out intersecting brush areas.\n"
+ " -noshare : Emit unique face edges instead of sharing them.\n"
+ " -notjunc : Don't fixup t-junctions.\n"
+ " -noopt : By default, vbsp removes the 'outer shell' of the map, which\n"
+ " are all the faces you can't see because you can never get\n"
+ " outside the map. -noopt disables this behaviour.\n"
+ " -noprune : Don't prune neighboring solid nodes.\n"
+ " -nomerge : Don't merge together chopped faces on nodes.\n"
+ " -nomergewater: Don't merge together chopped faces on water.\n"
+ " -nosubdiv : Don't subdivide faces for lightmapping.\n"
+ " -micro <#> : vbsp will warn when brushes are output with a volume less\n"
+ " than this number (default: 1.0).\n"
+ " -fulldetail : Mark all detail geometry as normal geometry (so all detail\n"
+ " geometry will affect visibility).\n"
+ " -leaktest : Stop processing the map if a leak is detected. Whether or not\n"
+ " this flag is set, a leak file will be written out at\n"
+ " <vmf filename>.lin, and it can be imported into Hammer.\n"
+ " -bumpall : Force all surfaces to be bump mapped.\n"
+ " -snapaxial : Snap axial planes to integer coordinates.\n"
+ " -block # # : Control the grid size mins that vbsp chops the level on.\n"
+ " -blocks # # # # : Enter the mins and maxs for the grid size vbsp uses.\n"
+ " -dumpstaticprops: Dump static props to staticprop*.txt\n"
+ " -dumpcollide : Write files with collision info.\n"
+ " -forceskyvis : Enable vis calculations in 3d skybox leaves\n"
+ " -luxelscale # : Scale all lightmaps by this amount (default: 1.0).\n"
+ " -minluxelscale #: No luxel scale will be lower than this amount (default: 1.0).\n"
+ " -lightifmissing : Force lightmaps to be generated for all surfaces even if\n"
+ " they don't need lightmaps.\n"
+ " -keepstalezip : Keep the BSP's zip files intact but regenerate everything\n"
+ " else.\n"
+ " -virtualdispphysics : Use virtual (not precomputed) displacement collision models\n"
+ " -xbox : Enable mandatory xbox options\n"
+ " -x360 : Generate Xbox360 version of vsp\n"
+ " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
+ " -replacematerials : Substitute materials according to materialsub.txt in content\\maps\n"
+ " -FullMinidumps : Write large minidumps on crash.\n"
+ );
+ }
+
+ DeleteCmdLine( argc, argv );
+ CmdLib_Cleanup();
+ CmdLib_Exit( 1 );
+ }
+
+ start = Plat_FloatTime();
+
+ // Run in the background?
+ if( g_bLowPriority )
+ {
+ SetLowPriority();
+ }
+
+ if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < 80 ) )
+ {
+ g_BumpAll = false;
+ }
+
+ if( g_luxelScale == 1.0f )
+ {
+ if ( g_nDXLevel == 70 )
+ {
+ g_luxelScale = 4.0f;
+ }
+ }
+
+ ThreadSetDefault ();
+ numthreads = 1; // multiple threads aren't helping...
+
+ // Setup the logfile.
+ char logFile[512];
+ _snprintf( logFile, sizeof(logFile), "%s.log", source );
+ SetSpewFunctionLogFile( logFile );
+
+ LoadPhysicsDLL();
+ LoadSurfaceProperties();
+
+#if 0
+ Msg( "qdir: %s This is the the path of the initial source file \n", qdir );
+ Msg( "gamedir: %s This is the base engine + mod-specific game dir (e.g. d:/tf2/mytfmod/) \n", gamedir );
+ Msg( "basegamedir: %s This is the base engine + base game directory (e.g. e:/hl2/hl2/, or d:/tf2/tf2/ )\n", basegamedir );
+#endif
+
+ sprintf( materialPath, "%smaterials", gamedir );
+ InitMaterialSystem( materialPath, CmdLib_GetFileSystemFactory() );
+ Msg( "materialPath: %s\n", materialPath );
+
+ // delete portal and line files
+ sprintf (path, "%s.prt", source);
+ remove (path);
+ sprintf (path, "%s.lin", source);
+ remove (path);
+
+ strcpy (name, ExpandArg (argv[i]));
+
+ const char *pszExtension = V_GetFileExtension( name );
+ if ( !pszExtension )
+ {
+ V_SetExtension( name, ".vmm", sizeof( name ) );
+ if ( !FileExists( name ) )
+ {
+ V_SetExtension( name, ".vmf", sizeof( name ) );
+ }
+ }
+
+ char platformBSPFileName[1024];
+ GetPlatformMapPath( source, platformBSPFileName, g_nDXLevel, 1024 );
+
+ // if we're combining materials, load the script file
+ if ( g_ReplaceMaterials )
+ {
+ LoadMaterialReplacementKeys( gamedir, mapbase );
+ }
+
+ //
+ // if onlyents, just grab the entites and resave
+ //
+ if (onlyents)
+ {
+ LoadBSPFile (platformBSPFileName);
+ num_entities = 0;
+ // Clear out the cubemap samples since they will be reparsed even with -onlyents
+ g_nCubemapSamples = 0;
+
+ // Mark as stale since the lighting could be screwed with new ents.
+ AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
+
+ LoadMapFile (name);
+ SetModelNumbers ();
+ SetLightStyles ();
+
+ // NOTE: If we ever precompute lighting for static props in
+ // vrad, EmitStaticProps should be removed here
+
+ // Emit static props found in the .vmf file
+ EmitStaticProps();
+
+ // NOTE: Don't deal with detail props here, it blows away lighting
+
+ // Recompute the skybox
+ ComputeBoundsNoSkybox();
+
+ // Make sure that we have a water lod control eneity if we have water in the map.
+ EnsurePresenceOfWaterLODControlEntity();
+
+ // Make sure the func_occluders have the appropriate data set
+ FixupOnlyEntsOccluderEntities();
+
+ // Doing this here because stuff abov may filter out entities
+ UnparseEntities ();
+
+ WriteBSPFile (platformBSPFileName);
+ }
+ else if (onlyprops)
+ {
+ // In the only props case, deal with static + detail props only
+ LoadBSPFile (platformBSPFileName);
+
+ LoadMapFile(name);
+ SetModelNumbers();
+ SetLightStyles();
+
+ // Emit static props found in the .vmf file
+ EmitStaticProps();
+
+ // Place detail props found in .vmf and based on material properties
+ LoadEmitDetailObjectDictionary( gamedir );
+ EmitDetailObjects();
+
+ WriteBSPFile (platformBSPFileName);
+ }
+ else
+ {
+ //
+ // start from scratch
+ //
+
+ // Load just the file system from the bsp
+ if( g_bKeepStaleZip && FileExists( platformBSPFileName ) )
+ {
+ LoadBSPFile_FileSystemOnly (platformBSPFileName);
+ // Mark as stale since the lighting could be screwed with new ents.
+ AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
+ }
+
+ LoadMapFile (name);
+ WorldVertexTransitionFixup();
+ if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
+ {
+ Cubemap_FixupBrushSidesMaterials();
+ Cubemap_AttachDefaultCubemapToSpecularSides();
+ Cubemap_AddUnreferencedCubemaps();
+ }
+ SetModelNumbers ();
+ SetLightStyles ();
+ LoadEmitDetailObjectDictionary( gamedir );
+ ProcessModels ();
+ }
+
+ end = Plat_FloatTime();
+
+ char str[512];
+ GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
+ Msg( "%s elapsed\n", str );
+
+ DeleteCmdLine( argc, argv );
+ ReleasePakFileLumps();
+ DeleteMaterialReplacementKeys();
+ ShutdownMaterialSystem();
+ CmdLib_Cleanup();
+ return 0;
+}
+
+
+/*
+=============
+main
+============
+*/
+int main (int argc, char **argv)
+{
+ // Install an exception handler.
+ SetupDefaultToolsMinidumpHandler();
+ return RunVBSP( argc, argv );
+}
+
+
diff --git a/mp/src/utils/vbsp/vbsp.h b/mp/src/utils/vbsp/vbsp.h new file mode 100644 index 00000000..9b994d38 --- /dev/null +++ b/mp/src/utils/vbsp/vbsp.h @@ -0,0 +1,657 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#if !defined( VBSP_H )
+#define VBSP_H
+
+
+#include "cmdlib.h"
+#include "mathlib/vector.h"
+#include "scriplib.h"
+#include "polylib.h"
+#include "threads.h"
+#include "bsplib.h"
+#include "qfiles.h"
+#include "utilmatlib.h"
+#include "ChunkFile.h"
+
+#ifdef WIN32
+#pragma warning( disable: 4706 )
+#endif
+
+class CUtlBuffer;
+
+#define MAX_BRUSH_SIDES 128
+#define CLIP_EPSILON 0.1
+
+#define TEXINFO_NODE -1 // side is allready on a node
+
+// this will output glview files for the given brushmodel. Brushmodel 1 is the world, 2 is the first brush entity, etc.
+#define DEBUG_BRUSHMODEL 0
+
+struct portal_t;
+struct node_t;
+
+struct plane_t : public dplane_t
+{
+ plane_t *hash_chain;
+
+ plane_t() { normal.Init(); }
+};
+
+
+struct brush_texture_t
+{
+ Vector UAxis;
+ Vector VAxis;
+ vec_t shift[2];
+ vec_t rotate;
+ vec_t textureWorldUnitsPerTexel[2];
+ vec_t lightmapWorldUnitsPerLuxel;
+ char name[TEXTURE_NAME_LENGTH];
+ int flags;
+
+ brush_texture_t() : UAxis(0,0,0), VAxis(0,0,0) {}
+};
+
+struct mapdispinfo_t;
+
+struct side_t
+{
+ int planenum;
+ int texinfo;
+ mapdispinfo_t *pMapDisp;
+
+ winding_t *winding;
+ side_t *original; // bspbrush_t sides will reference the mapbrush_t sides
+ int contents; // from miptex
+ int surf; // from miptex
+ qboolean visible; // choose visble planes first
+ qboolean tested; // this plane allready checked as a split
+ qboolean bevel; // don't ever use for bsp splitting
+
+ side_t *next;
+ int origIndex;
+ int id; // This is the unique id generated by worldcraft for this side.
+ unsigned int smoothingGroups;
+ CUtlVector<int> aOverlayIds; // List of overlays that reside on this side.
+ CUtlVector<int> aWaterOverlayIds; // List of water overlays that reside on this side.
+ bool m_bDynamicShadowsEnabled; // Goes into dface_t::SetDynamicShadowsEnabled().
+};
+
+struct mapbrush_t
+{
+ int entitynum;
+ int brushnum;
+ int id; // The unique ID of this brush in the editor, used for reporting errors.
+ int contents;
+ Vector mins, maxs;
+ int numsides;
+ side_t *original_sides;
+};
+
+#define PLANENUM_LEAF -1
+
+#define MAXEDGES 32
+
+struct face_t
+{
+ int id;
+
+ face_t *next; // on node
+
+ // the chain of faces off of a node can be merged or split,
+ // but each face_t along the way will remain in the chain
+ // until the entire tree is freed
+ face_t *merged; // if set, this face isn't valid anymore
+ face_t *split[2]; // if set, this face isn't valid anymore
+
+ portal_t *portal;
+ int texinfo;
+ int dispinfo;
+ // This is only for surfaces that are the boundaries of fog volumes
+ // (ie. water surfaces)
+ // All of the rest of the surfaces can look at their leaf to find out
+ // what fog volume they are in.
+ node_t *fogVolumeLeaf;
+
+ int planenum;
+ int contents; // faces in different contents can't merge
+ int outputnumber;
+ winding_t *w;
+ int numpoints;
+ qboolean badstartvert; // tjunctions cannot be fixed without a midpoint vertex
+ int vertexnums[MAXEDGES];
+ side_t *originalface; // save the "side" this face came from
+ int firstPrimID;
+ int numPrims;
+ unsigned int smoothingGroups;
+};
+
+void EmitFace( face_t *f, qboolean onNode );
+
+struct mapdispinfo_t
+{
+ face_t face;
+ int entitynum;
+ int power;
+ int minTess;
+ float smoothingAngle;
+ Vector uAxis;
+ Vector vAxis;
+ Vector startPosition;
+ float alphaValues[MAX_DISPVERTS];
+ float maxDispDist;
+ float dispDists[MAX_DISPVERTS];
+ Vector vectorDisps[MAX_DISPVERTS];
+ Vector vectorOffsets[MAX_DISPVERTS];
+ int contents;
+ int brushSideID;
+ unsigned short triTags[MAX_DISPTRIS];
+ int flags;
+
+#ifdef VSVMFIO
+ float m_elevation; // "elevation"
+ Vector m_offsetNormals[ MAX_DISPTRIS ]; // "offset_normals"
+#endif // VSVMFIO
+
+};
+
+extern int nummapdispinfo;
+extern mapdispinfo_t mapdispinfo[MAX_MAP_DISPINFO];
+
+extern float g_defaultLuxelSize;
+extern float g_luxelScale;
+extern float g_minLuxelScale;
+extern bool g_BumpAll;
+extern int g_nDXLevel;
+
+int GetDispInfoEntityNum( mapdispinfo_t *pDisp );
+void ComputeBoundsNoSkybox( );
+
+struct bspbrush_t
+{
+ int id;
+ bspbrush_t *next;
+ Vector mins, maxs;
+ int side, testside; // side of node during construction
+ mapbrush_t *original;
+ int numsides;
+ side_t sides[6]; // variably sized
+};
+
+
+#define MAX_NODE_BRUSHES 8
+
+struct leafface_t
+{
+ face_t *pFace;
+ leafface_t *pNext;
+};
+
+struct node_t
+{
+ int id;
+
+ // both leafs and nodes
+ int planenum; // -1 = leaf node
+ node_t *parent;
+ Vector mins, maxs; // valid after portalization
+ bspbrush_t *volume; // one for each leaf/node
+
+ // nodes only
+ side_t *side; // the side that created the node
+ node_t *children[2];
+ face_t *faces; // these are the cutup ones that live in the plane of "side".
+
+ // leafs only
+ bspbrush_t *brushlist; // fragments of all brushes in this leaf
+ leafface_t *leaffacelist;
+ int contents; // OR of all brush contents
+ int occupied; // 1 or greater can reach entity
+ entity_t *occupant; // for leak file testing
+ int cluster; // for portalfile writing
+ int area; // for areaportals
+ portal_t *portals; // also on nodes during construction
+ int diskId; // dnodes or dleafs index after this has been emitted
+};
+
+
+struct portal_t
+{
+ int id;
+ plane_t plane;
+ node_t *onnode; // NULL = outside box
+ node_t *nodes[2]; // [0] = front side of plane
+ portal_t *next[2];
+ winding_t *winding;
+ qboolean sidefound; // false if ->side hasn't been checked
+ side_t *side; // NULL = non-visible
+ face_t *face[2]; // output face in bsp file
+};
+
+
+struct tree_t
+{
+ node_t *headnode;
+ node_t outside_node;
+ Vector mins, maxs;
+ bool leaked;
+};
+
+
+extern int entity_num;
+
+struct LoadSide_t;
+struct LoadEntity_t;
+class CManifest;
+
+class CMapFile
+{
+public:
+ CMapFile( void ) { Init(); }
+
+ void Init( void );
+
+ void AddPlaneToHash (plane_t *p);
+ int CreateNewFloatPlane (Vector& normal, vec_t dist);
+ int FindFloatPlane (Vector& normal, vec_t dist);
+ int PlaneFromPoints(const Vector &p0, const Vector &p1, const Vector &p2);
+ void AddBrushBevels (mapbrush_t *b);
+ qboolean MakeBrushWindings (mapbrush_t *ob);
+ void MoveBrushesToWorld( entity_t *mapent );
+ void MoveBrushesToWorldGeneral( entity_t *mapent );
+ void RemoveContentsDetailFromEntity( entity_t *mapent );
+ int SideIDToIndex( int brushSideID );
+ void AddLadderKeys( entity_t *mapent );
+ ChunkFileResult_t LoadEntityCallback(CChunkFile *pFile, int nParam);
+ void ForceFuncAreaPortalWindowContents();
+ ChunkFileResult_t LoadSideCallback(CChunkFile *pFile, LoadSide_t *pSideInfo);
+ ChunkFileResult_t LoadConnectionsKeyCallback(const char *szKey, const char *szValue, LoadEntity_t *pLoadEntity);
+ ChunkFileResult_t LoadSolidCallback(CChunkFile *pFile, LoadEntity_t *pLoadEntity);
+ void TestExpandBrushes(void);
+
+ static char m_InstancePath[ MAX_PATH ];
+ static void SetInstancePath( const char *pszInstancePath );
+ static const char *GetInstancePath( void ) { return m_InstancePath; }
+ static bool DeterminePath( const char *pszBaseFileName, const char *pszInstanceFileName, char *pszOutFileName );
+
+ void CheckForInstances( const char *pszFileName );
+ void MergeInstance( entity_t *pInstanceEntity, CMapFile *Instance );
+ void MergePlanes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void MergeBrushes( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void MergeBrushSides( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void ReplaceInstancePair( epair_t *pPair, entity_t *pInstanceEntity );
+ void MergeEntities( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+ void MergeOverlays( entity_t *pInstanceEntity, CMapFile *Instance, Vector &InstanceOrigin, QAngle &InstanceAngle, matrix3x4_t &InstanceMatrix );
+
+ static int m_InstanceCount;
+ static int c_areaportals;
+
+ plane_t mapplanes[MAX_MAP_PLANES];
+ int nummapplanes;
+
+ #define PLANE_HASHES 1024
+ plane_t *planehash[PLANE_HASHES];
+
+ int nummapbrushes;
+ mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+ Vector map_mins, map_maxs;
+
+ int nummapbrushsides;
+ side_t brushsides[MAX_MAP_BRUSHSIDES];
+
+ brush_texture_t side_brushtextures[MAX_MAP_BRUSHSIDES];
+
+ int num_entities;
+ entity_t entities[MAX_MAP_ENTITIES];
+
+ int c_boxbevels;
+ int c_edgebevels;
+ int c_clipbrushes;
+ int g_ClipTexinfo;
+
+ class CConnectionPairs
+ {
+ public:
+ CConnectionPairs( epair_t *pair, CConnectionPairs *next )
+ {
+ m_Pair = pair;
+ m_Next = next;
+ }
+
+ epair_t *m_Pair;
+ CConnectionPairs *m_Next;
+ };
+
+ CConnectionPairs *m_ConnectionPairs;
+
+ int m_StartMapOverlays;
+ int m_StartMapWaterOverlays;
+};
+
+extern CMapFile *g_MainMap;
+extern CMapFile *g_LoadingMap;
+
+extern CUtlVector< CMapFile * > g_Maps;
+
+extern int g_nMapFileVersion;
+
+extern qboolean noprune;
+extern qboolean nodetail;
+extern qboolean fulldetail;
+extern qboolean nomerge;
+extern qboolean nomergewater;
+extern qboolean nosubdiv;
+extern qboolean nowater;
+extern qboolean noweld;
+extern qboolean noshare;
+extern qboolean notjunc;
+extern qboolean nocsg;
+extern qboolean noopt;
+extern qboolean dumpcollide;
+extern qboolean nodetailcuts;
+extern qboolean g_DumpStaticProps;
+extern qboolean g_bSkyVis;
+extern vec_t microvolume;
+extern bool g_snapAxialPlanes;
+extern bool g_NodrawTriggers;
+extern bool g_DisableWaterLighting;
+extern bool g_bAllowDetailCracks;
+extern bool g_bNoVirtualMesh;
+extern char outbase[32];
+
+extern char source[1024];
+extern char mapbase[ 64 ];
+extern CUtlVector<int> g_SkyAreas;
+
+bool LoadMapFile( const char *pszFileName );
+int GetVertexnum( Vector& v );
+bool Is3DSkyboxArea( int area );
+
+//=============================================================================
+
+// textures.c
+
+struct textureref_t
+{
+ char name[TEXTURE_NAME_LENGTH];
+ int flags;
+ float lightmapWorldUnitsPerLuxel;
+ int contents;
+};
+
+extern textureref_t textureref[MAX_MAP_TEXTURES];
+
+int FindMiptex (const char *name);
+
+int TexinfoForBrushTexture (plane_t *plane, brush_texture_t *bt, const Vector& origin);
+int GetSurfaceProperties2( MaterialSystemMaterial_t matID, const char *pMatName );
+
+extern int g_SurfaceProperties[MAX_MAP_TEXDATA];
+void LoadSurfaceProperties( void );
+
+int PointLeafnum ( dmodel_t* pModel, const Vector& p );
+
+//=============================================================================
+
+void FindGCD (int *v);
+
+mapbrush_t *Brush_LoadEntity (entity_t *ent);
+int PlaneTypeForNormal (Vector& normal);
+qboolean MakeBrushPlanes (mapbrush_t *b);
+int FindIntPlane (int *inormal, int *iorigin);
+void CreateBrush (int brushnum);
+
+
+//=============================================================================
+// detail objects
+//=============================================================================
+
+void LoadEmitDetailObjectDictionary( char const* pGameDir );
+void EmitDetailObjects();
+
+//=============================================================================
+// static props
+//=============================================================================
+
+void EmitStaticProps();
+bool LoadStudioModel( char const* pFileName, char const* pEntityType, CUtlBuffer& buf );
+
+//=============================================================================
+//=============================================================================
+// procedurally created .vmt files
+//=============================================================================
+
+void EmitStaticProps();
+
+// draw.c
+
+extern Vector draw_mins, draw_maxs;
+extern bool g_bLightIfMissing;
+
+void Draw_ClearWindow (void);
+void DrawWinding (winding_t *w);
+
+void GLS_BeginScene (void);
+void GLS_Winding (winding_t *w, int code);
+void GLS_EndScene (void);
+
+//=============================================================================
+
+// csg
+
+enum detailscreen_e
+{
+ FULL_DETAIL = 0,
+ ONLY_DETAIL = 1,
+ NO_DETAIL = 2,
+};
+
+#define TRANSPARENT_CONTENTS (CONTENTS_GRATE|CONTENTS_WINDOW)
+
+#include "csg.h"
+
+//=============================================================================
+
+// brushbsp
+
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
+
+bspbrush_t *CopyBrush (bspbrush_t *brush);
+
+void SplitBrush (bspbrush_t *brush, int planenum,
+ bspbrush_t **front, bspbrush_t **back);
+
+tree_t *AllocTree (void);
+node_t *AllocNode (void);
+bspbrush_t *AllocBrush (int numsides);
+int CountBrushList (bspbrush_t *brushes);
+void FreeBrush (bspbrush_t *brushes);
+vec_t BrushVolume (bspbrush_t *brush);
+node_t *NodeForPoint (node_t *node, Vector& origin);
+
+void BoundBrush (bspbrush_t *brush);
+void FreeBrushList (bspbrush_t *brushes);
+node_t *PointInLeaf (node_t *node, Vector& point);
+
+tree_t *BrushBSP (bspbrush_t *brushlist, Vector& mins, Vector& maxs);
+
+#define PSIDE_FRONT 1
+#define PSIDE_BACK 2
+#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
+#define PSIDE_FACING 4
+int BrushBspBoxOnPlaneSide (const Vector& mins, const Vector& maxs, dplane_t *plane);
+extern qboolean WindingIsTiny (winding_t *w);
+
+//=============================================================================
+
+// portals.c
+
+int VisibleContents (int contents);
+
+void MakeHeadnodePortals (tree_t *tree);
+void MakeNodePortal (node_t *node);
+void SplitNodePortals (node_t *node);
+
+qboolean Portal_VisFlood (portal_t *p);
+
+qboolean FloodEntities (tree_t *tree);
+void FillOutside (node_t *headnode);
+void FloodAreas (tree_t *tree);
+void MarkVisibleSides (tree_t *tree, int start, int end, int detailScreen);
+void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount );
+void FreePortal (portal_t *p);
+void EmitAreaPortals (node_t *headnode);
+
+void MakeTreePortals (tree_t *tree);
+
+//=============================================================================
+
+// glfile.c
+
+void OutputWinding (winding_t *w, FileHandle_t glview);
+void OutputWindingColor (winding_t *w, FileHandle_t glview, int r, int g, int b);
+void WriteGLView (tree_t *tree, char *source);
+void WriteGLViewFaces (tree_t *tree, const char *source);
+void WriteGLViewBrushList( bspbrush_t *pList, const char *pName );
+//=============================================================================
+
+// leakfile.c
+
+void LeakFile (tree_t *tree);
+void AreaportalLeakFile( tree_t *tree, portal_t *pStartPortal, portal_t *pEndPortal, node_t *pStart );
+
+//=============================================================================
+
+// prtfile.c
+
+void AddVisCluster( entity_t *pFuncVisCluster );
+void WritePortalFile (tree_t *tree);
+
+//=============================================================================
+
+// writebsp.c
+
+void SetModelNumbers (void);
+void SetLightStyles (void);
+
+void BeginBSPFile (void);
+void WriteBSP (node_t *headnode, face_t *pLeafFaceList);
+void EndBSPFile (void);
+void BeginModel (void);
+void EndModel (void);
+
+extern int firstmodeledge;
+extern int firstmodelface;
+
+//=============================================================================
+
+// faces.c
+
+void MakeFaces (node_t *headnode);
+void MakeDetailFaces (node_t *headnode);
+face_t *FixTjuncs( node_t *headnode, face_t *pLeafFaceList );
+
+face_t *AllocFace (void);
+void FreeFace (face_t *f);
+void FreeFaceList( face_t *pFaces );
+
+void MergeFaceList(face_t **pFaceList);
+void SubdivideFaceList(face_t **pFaceList);
+
+extern face_t *edgefaces[MAX_MAP_EDGES][2];
+
+
+//=============================================================================
+
+// tree.c
+
+void FreeTree (tree_t *tree);
+void FreeTree_r (node_t *node);
+void PrintTree_r (node_t *node, int depth);
+void FreeTreePortals_r (node_t *node);
+void PruneNodes_r (node_t *node);
+void PruneNodes (node_t *node);
+
+// Returns true if the entity is a func_occluder
+bool IsFuncOccluder( int entity_num );
+
+
+//=============================================================================
+// ivp.cpp
+class CPhysCollide;
+void EmitPhysCollision();
+void DumpCollideToGlView( CPhysCollide *pCollide, const char *pFilename );
+void EmitWaterVolumesForBSP( dmodel_t *pModel, node_t *headnode );
+
+//=============================================================================
+// find + find or create the texdata
+int FindTexData( const char *pName );
+int FindOrCreateTexData( const char *pName );
+// Add a clone of an existing texdata with a new name
+int AddCloneTexData( dtexdata_t *pExistingTexData, char const *cloneTexDataName );
+int FindOrCreateTexInfo( const texinfo_t &searchTexInfo );
+int FindAliasedTexData( const char *pName, dtexdata_t *sourceTexture );
+int FindTexInfo( const texinfo_t &searchTexInfo );
+
+//=============================================================================
+// normals.c
+void SaveVertexNormals( void );
+
+//=============================================================================
+// cubemap.cpp
+void Cubemap_InsertSample( const Vector& origin, int size );
+void Cubemap_CreateDefaultCubemaps( void );
+void Cubemap_SaveBrushSides( const char *pSideListStr );
+void Cubemap_FixupBrushSidesMaterials( void );
+void Cubemap_AttachDefaultCubemapToSpecularSides( void );
+// Add skipped cubemaps that are referenced by the engine
+void Cubemap_AddUnreferencedCubemaps( void );
+
+//=============================================================================
+// overlay.cpp
+#define OVERLAY_MAP_STRLEN 256
+
+struct mapoverlay_t
+{
+ int nId;
+ unsigned short m_nRenderOrder;
+ char szMaterialName[OVERLAY_MAP_STRLEN];
+ float flU[2];
+ float flV[2];
+ float flFadeDistMinSq;
+ float flFadeDistMaxSq;
+ Vector vecUVPoints[4];
+ Vector vecOrigin;
+ Vector vecBasis[3];
+ CUtlVector<int> aSideList;
+ CUtlVector<int> aFaceList;
+};
+
+extern CUtlVector<mapoverlay_t> g_aMapOverlays;
+extern CUtlVector<mapoverlay_t> g_aMapWaterOverlays;
+
+int Overlay_GetFromEntity( entity_t *pMapEnt );
+void Overlay_UpdateSideLists( int StartIndex );
+void Overlay_AddFaceToLists( int iFace, side_t *pSide );
+void Overlay_EmitOverlayFaces( void );
+void OverlayTransition_UpdateSideLists( int StartIndex );
+void OverlayTransition_AddFaceToLists( int iFace, side_t *pSide );
+void OverlayTransition_EmitOverlayFaces( void );
+void Overlay_Translate( mapoverlay_t *pOverlay, Vector &OriginOffset, QAngle &AngleOffset, matrix3x4_t &Matrix );
+
+//=============================================================================
+
+void RemoveAreaPortalBrushes_R( node_t *node );
+
+dtexdata_t *GetTexData( int index );
+
+#endif
+
diff --git a/mp/src/utils/vbsp/worldvertextransitionfixup.cpp b/mp/src/utils/vbsp/worldvertextransitionfixup.cpp new file mode 100644 index 00000000..5d00f3f5 --- /dev/null +++ b/mp/src/utils/vbsp/worldvertextransitionfixup.cpp @@ -0,0 +1,212 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "bsplib.h"
+#include "vbsp.h"
+#include "tier1/UtlBuffer.h"
+#include "tier1/utlvector.h"
+#include "KeyValues.h"
+#include "materialpatch.h"
+
+struct entitySideList_t
+{
+ int firstBrushSide;
+ int brushSideCount;
+};
+
+static bool SideIsNotDispAndHasDispMaterial( int iSide )
+{
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+
+ // If it's a displacement, then it's fine to have a displacement-only material.
+ if ( pSide->pMapDisp )
+ {
+ return false;
+ }
+
+ pSide->texinfo;
+
+ return true;
+}
+
+static void BackSlashToForwardSlash( char *pname )
+{
+ while ( *pname ) {
+ if ( *pname == '\\' )
+ *pname = '/';
+ pname++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Generate patched material name
+//-----------------------------------------------------------------------------
+static void GeneratePatchedMaterialName( const char *pMaterialName, char *pBuffer, int nMaxLen )
+{
+ int nLen = Q_snprintf( pBuffer, nMaxLen, "maps/%s/%s_wvt_patch", mapbase, pMaterialName );
+
+ Assert( nLen < TEXTURE_NAME_LENGTH - 1 );
+ if ( nLen >= TEXTURE_NAME_LENGTH - 1 )
+ {
+ Error( "Generated worldvertextransition patch name : %s too long! (max = %d)\n", pBuffer, TEXTURE_NAME_LENGTH );
+ }
+
+ BackSlashToForwardSlash( pBuffer );
+ Q_strlower( pBuffer );
+}
+
+static void RemoveKey( KeyValues *kv, const char *pSubKeyName )
+{
+ KeyValues *pSubKey = kv->FindKey( pSubKeyName );
+ if( pSubKey )
+ {
+ kv->RemoveSubKey( pSubKey );
+ pSubKey->deleteThis();
+ }
+}
+
+void CreateWorldVertexTransitionPatchedMaterial( const char *pOriginalMaterialName, const char *pPatchedMaterialName )
+{
+ KeyValues *kv = LoadMaterialKeyValues( pOriginalMaterialName, 0 );
+ if( kv )
+ {
+ // change shader to Lightmappedgeneric (from worldvertextransition*)
+ kv->SetName( "LightmappedGeneric" );
+ // don't need no stinking $basetexture2 or any other second texture vars
+ RemoveKey( kv, "$basetexture2" );
+ RemoveKey( kv, "$bumpmap2" );
+ RemoveKey( kv, "$bumpframe2" );
+ RemoveKey( kv, "$basetexture2noenvmap" );
+ RemoveKey( kv, "$blendmodulatetexture" );
+ RemoveKey( kv, "$maskedblending" );
+ RemoveKey( kv, "$surfaceprop2" );
+ // If we didn't want a basetexture on the first texture in the blend, we don't want an envmap at all.
+ KeyValues *basetexturenoenvmap = kv->FindKey( "$BASETEXTURENOENVMAP" );
+ if( basetexturenoenvmap->GetInt() )
+ {
+ RemoveKey( kv, "$envmap" );
+ }
+
+ Warning( "Patching WVT material: %s\n", pPatchedMaterialName );
+ WriteMaterialKeyValuesToPak( pPatchedMaterialName, kv );
+ }
+}
+
+int CreateBrushVersionOfWorldVertexTransitionMaterial( int originalTexInfo )
+{
+ // Don't make cubemap tex infos for nodes
+ if ( originalTexInfo == TEXINFO_NODE )
+ return originalTexInfo;
+
+ texinfo_t *pTexInfo = &texinfo[originalTexInfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pOriginalMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+
+ // Get out of here if the originalTexInfo is already a patched wvt material
+ if ( Q_stristr( pOriginalMaterialName, "_wvt_patch" ) )
+ return originalTexInfo;
+
+ char patchedMaterialName[1024];
+ GeneratePatchedMaterialName( pOriginalMaterialName, patchedMaterialName, 1024 );
+// Warning( "GeneratePatchedMaterialName: %s %s\n", pMaterialName, patchedMaterialName );
+
+ // Make sure the texdata doesn't already exist.
+ int nTexDataID = FindTexData( patchedMaterialName );
+ bool bHasTexData = (nTexDataID != -1);
+ if( !bHasTexData )
+ {
+ // Create the new vmt material file
+ CreateWorldVertexTransitionPatchedMaterial( pOriginalMaterialName, patchedMaterialName );
+
+ // Make a new texdata
+ nTexDataID = AddCloneTexData( pTexData, patchedMaterialName );
+ }
+
+ Assert( nTexDataID != -1 );
+
+ texinfo_t newTexInfo;
+ newTexInfo = *pTexInfo;
+ newTexInfo.texdata = nTexDataID;
+
+ int nTexInfoID = -1;
+
+ // See if we need to make a new texinfo
+ bool bHasTexInfo = false;
+ if( bHasTexData )
+ {
+ nTexInfoID = FindTexInfo( newTexInfo );
+ bHasTexInfo = (nTexInfoID != -1);
+ }
+
+ // Make a new texinfo if we need to.
+ if( !bHasTexInfo )
+ {
+ nTexInfoID = texinfo.AddToTail( newTexInfo );
+ }
+
+ Assert( nTexInfoID != -1 );
+ return nTexInfoID;
+}
+
+const char *GetShaderNameForTexInfo( int iTexInfo )
+{
+ texinfo_t *pTexInfo = &texinfo[iTexInfo];
+ dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
+ const char *pMaterialName = TexDataStringTable_GetString( pTexData->nameStringTableID );
+ MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
+ const char *pShaderName = GetMaterialShaderName( hMaterial );
+ return pShaderName;
+}
+
+void WorldVertexTransitionFixup( void )
+{
+ CUtlVector<entitySideList_t> sideList;
+ sideList.SetCount( g_MainMap->num_entities );
+ int i;
+ for ( i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ sideList[i].firstBrushSide = 0;
+ sideList[i].brushSideCount = 0;
+ }
+
+ for ( i = 0; i < g_MainMap->nummapbrushes; i++ )
+ {
+ sideList[g_MainMap->mapbrushes[i].entitynum].brushSideCount += g_MainMap->mapbrushes[i].numsides;
+ }
+ int curSide = 0;
+ for ( i = 0; i < g_MainMap->num_entities; i++ )
+ {
+ sideList[i].firstBrushSide = curSide;
+ curSide += sideList[i].brushSideCount;
+ }
+
+ int currentEntity = 0;
+ for ( int iSide = 0; iSide < g_MainMap->nummapbrushsides; ++iSide )
+ {
+ side_t *pSide = &g_MainMap->brushsides[iSide];
+
+ // skip displacments
+ if ( pSide->pMapDisp )
+ continue;
+
+ if( pSide->texinfo < 0 )
+ continue;
+
+ const char *pShaderName = GetShaderNameForTexInfo( pSide->texinfo );
+ if ( !pShaderName || !Q_stristr( pShaderName, "worldvertextransition" ) )
+ {
+ continue;
+ }
+
+ while ( currentEntity < g_MainMap->num_entities-1 &&
+ iSide > sideList[currentEntity].firstBrushSide + sideList[currentEntity].brushSideCount )
+ {
+ currentEntity++;
+ }
+
+ pSide->texinfo = CreateBrushVersionOfWorldVertexTransitionMaterial( pSide->texinfo );
+ }
+}
\ No newline at end of file diff --git a/mp/src/utils/vbsp/worldvertextransitionfixup.h b/mp/src/utils/vbsp/worldvertextransitionfixup.h new file mode 100644 index 00000000..6297dca5 --- /dev/null +++ b/mp/src/utils/vbsp/worldvertextransitionfixup.h @@ -0,0 +1,15 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef WORLDVERTEXTRANSITIONFIXUP_H
+#define WORLDVERTEXTRANSITIONFIXUP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+void WorldVertexTransitionFixup( void );
+
+#endif // WORLDVERTEXTRANSITIONFIXUP_H
diff --git a/mp/src/utils/vbsp/writebsp.cpp b/mp/src/utils/vbsp/writebsp.cpp new file mode 100644 index 00000000..c776bede --- /dev/null +++ b/mp/src/utils/vbsp/writebsp.cpp @@ -0,0 +1,1552 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vbsp.h"
+#include "disp_vbsp.h"
+#include "utlvector.h"
+#include "faces.h"
+#include "builddisp.h"
+#include "tier1/strtools.h"
+#include "utilmatlib.h"
+#include "utldict.h"
+#include "map.h"
+
+int c_nofaces;
+int c_facenodes;
+
+// NOTE: This is a global used to link faces back to the tree node/portals they came from
+// it's used when filling water volumes
+node_t *dfacenodes[MAX_MAP_FACES];
+
+
+/*
+=========================================================
+
+ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
+
+=========================================================
+*/
+
+void EmitFaceVertexes (face_t **list, face_t *f);
+void AssignOccluderAreas();
+
+/*
+============
+EmitPlanes
+
+There is no oportunity to discard planes, because all of the original
+brushes will be saved in the map.
+============
+*/
+void EmitPlanes (void)
+{
+ int i;
+ dplane_t *dp;
+ plane_t *mp;
+ int planetranslate[MAX_MAP_PLANES];
+
+ mp = g_MainMap->mapplanes;
+ for (i=0 ; i<g_MainMap->nummapplanes ; i++, mp++)
+ {
+ dp = &dplanes[numplanes];
+ planetranslate[i] = numplanes;
+ VectorCopy ( mp->normal, dp->normal);
+ dp->dist = mp->dist;
+ dp->type = mp->type;
+ numplanes++;
+ }
+}
+
+
+//========================================================
+
+void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
+{
+ int i;
+ int facenum;
+
+ while (f->merged)
+ f = f->merged;
+
+ if (f->split[0])
+ {
+ EmitMarkFace (leaf_p, f->split[0]);
+ EmitMarkFace (leaf_p, f->split[1]);
+ return;
+ }
+
+ facenum = f->outputnumber;
+ if (facenum == -1)
+ return; // degenerate face
+
+ if (facenum < 0 || facenum >= numfaces)
+ Error ("Bad leafface");
+ for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
+ if (dleaffaces[i] == facenum)
+ break; // merged out face
+ if (i == numleaffaces)
+ {
+ if (numleaffaces >= MAX_MAP_LEAFFACES)
+ Error ("Too many detail brush faces, max = %d\n", MAX_MAP_LEAFFACES);
+
+ dleaffaces[numleaffaces] = facenum;
+ numleaffaces++;
+ }
+
+}
+
+
+/*
+==================
+EmitLeaf
+==================
+*/
+void EmitLeaf (node_t *node)
+{
+ dleaf_t *leaf_p;
+ portal_t *p;
+ int s;
+ face_t *f;
+ bspbrush_t *b;
+ int i;
+ int brushnum;
+ leafface_t *pList;
+
+ // emit a leaf
+ if (numleafs >= MAX_MAP_LEAFS)
+ Error ("Too many BSP leaves, max = %d", MAX_MAP_LEAFS);
+
+ node->diskId = numleafs;
+ leaf_p = &dleafs[numleafs];
+ numleafs++;
+
+ if( nummodels == 0 )
+ {
+ leaf_p->cluster = node->cluster;
+ }
+ else
+ {
+ // Submodels don't have clusters. If this isn't set to -1 here, then there
+ // will be multiple leaves (albeit from different models) that reference
+ // the same cluster and parts of the code like ivp.cpp's ConvertWaterModelToPhysCollide
+ // won't work.
+ leaf_p->cluster = -1;
+ }
+
+ leaf_p->contents = node->contents;
+ leaf_p->area = node->area;
+
+ // By default, assume the leaf can see the skybox.
+ // VRAD will do the actual computation to see if it really can see the skybox
+ leaf_p->flags = LEAF_FLAGS_SKY;
+
+ //
+ // write bounding box info
+ //
+ VECTOR_COPY (node->mins, leaf_p->mins);
+ VECTOR_COPY (node->maxs, leaf_p->maxs);
+
+ //
+ // write the leafbrushes
+ //
+ leaf_p->firstleafbrush = numleafbrushes;
+ for (b=node->brushlist ; b ; b=b->next)
+ {
+ if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
+ Error ("Too many brushes in one leaf, max = %d", MAX_MAP_LEAFBRUSHES);
+
+ brushnum = b->original - g_MainMap->mapbrushes;
+ for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
+ {
+ if (dleafbrushes[i] == brushnum)
+ break;
+ }
+
+ if (i == numleafbrushes)
+ {
+ dleafbrushes[numleafbrushes] = brushnum;
+ numleafbrushes++;
+ }
+ }
+ leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
+
+ //
+ // write the leaffaces
+ //
+ if (leaf_p->contents & CONTENTS_SOLID)
+ return; // no leaffaces in solids
+
+ leaf_p->firstleafface = numleaffaces;
+
+ for (p = node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ f = p->face[s];
+ if (!f)
+ continue; // not a visible portal
+
+ EmitMarkFace (leaf_p, f);
+ }
+
+ // emit the detail faces
+ for ( pList = node->leaffacelist; pList; pList = pList->pNext )
+ {
+ EmitMarkFace( leaf_p, pList->pFace );
+ }
+
+
+ leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
+}
+
+// per face plane - original face "side" list
+side_t *pOrigFaceSideList[MAX_MAP_PLANES];
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int CreateOrigFace( face_t *f )
+{
+ int i, j;
+ dface_t *of;
+ side_t *side;
+ int vIndices[128];
+ int eIndex[2];
+ winding_t *pWinding;
+
+ // not a real face!
+ if( !f->w )
+ return -1;
+
+ // get the original face -- the "side"
+ side = f->originalface;
+
+ // get the original face winding
+ if( !side->winding )
+ {
+ return -1;
+ }
+
+ //
+ // get the next original face
+ //
+ if( numorigfaces >= MAX_MAP_FACES )
+ Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
+ of = &dorigfaces[numorigfaces];
+ numorigfaces++;
+
+ // set original face to -1 -- it is an origianl face!
+ of->origFace = -1;
+
+ //
+ // add side to plane list
+ //
+ side->next = pOrigFaceSideList[f->planenum];
+ pOrigFaceSideList[f->planenum] = side;
+ side->origIndex = numorigfaces - 1;
+
+ pWinding = CopyWinding( side->winding );
+
+ //
+ // plane info
+ //
+ of->planenum = side->planenum;
+ if ( side->contents & CONTENTS_DETAIL )
+ of->onNode = 0;
+ else
+ of->onNode = 1;
+ of->side = side->planenum & 1;
+
+ //
+ // edge info
+ //
+ of->firstedge = numsurfedges;
+ of->numedges = side->winding->numpoints;
+
+ //
+ // material info
+ //
+ of->texinfo = side->texinfo;
+ of->dispinfo = f->dispinfo;
+
+ //
+ // save the vertices
+ //
+ for( i = 0; i < pWinding->numpoints; i++ )
+ {
+ //
+ // compare vertices
+ //
+ vIndices[i] = GetVertexnum( pWinding->p[i] );
+ }
+
+ //
+ // save off points -- as edges
+ //
+ for( i = 0; i < pWinding->numpoints; i++ )
+ {
+ //
+ // look for matching edges first
+ //
+ eIndex[0] = vIndices[i];
+ eIndex[1] = vIndices[(i+1)%pWinding->numpoints];
+
+ for( j = firstmodeledge; j < numedges; j++ )
+ {
+ if( ( eIndex[0] == dedges[j].v[1] ) &&
+ ( eIndex[1] == dedges[j].v[0] ) &&
+ ( edgefaces[j][0]->contents == f->contents ) )
+ {
+ // check for multiple backward edges!! -- shouldn't have
+ if( edgefaces[j][1] )
+ continue;
+
+ // set back edge
+ edgefaces[j][1] = f;
+
+ //
+ // get next surface edge
+ //
+ if( numsurfedges >= MAX_MAP_SURFEDGES )
+ Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
+ dsurfedges[numsurfedges] = -j;
+ numsurfedges++;
+ break;
+ }
+ }
+
+ if( j == numedges )
+ {
+ //
+ // get next edge
+ //
+ AddEdge( eIndex[0], eIndex[1], f );
+
+ //
+ // get next surface edge
+ //
+ if( numsurfedges >= MAX_MAP_SURFEDGES )
+ Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
+ dsurfedges[numsurfedges] = ( numedges - 1 );
+ numsurfedges++;
+ }
+ }
+
+ // return the index
+ return ( numorigfaces - 1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: search for a face within the origface list and return the index if
+// found
+// Input: f - the face to compare
+// Output: the index of the face it found, -1 if not found
+//-----------------------------------------------------------------------------
+int FindOrigFace( face_t *f )
+{
+ int i;
+ static int bClear = 0;
+ side_t *pSide;
+
+ //
+ // initially clear the face side lists (per face plane)
+ //
+ if( !bClear )
+ {
+ for( i = 0; i < MAX_MAP_PLANES; i++ )
+ {
+ pOrigFaceSideList[i] = NULL;
+ }
+ bClear = 1;
+ }
+
+ //
+ // compare the sides
+ //
+ for( pSide = pOrigFaceSideList[f->planenum]; pSide; pSide = pSide->next )
+ {
+ if( pSide == f->originalface )
+ return pSide->origIndex;
+ }
+
+ // original face not found in list
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: to find an the original face within the list of original faces, if
+// a match is not found then create a new origFace -- either way pass
+// back the index of the origface in the list
+// Input: f - face containing the original face information
+// Output: the index of the origface in the origface list
+//-----------------------------------------------------------------------------
+int FindOrCreateOrigFace( face_t *f )
+{
+ int index;
+
+ // check for an original face
+ if( !f->originalface )
+ return -1;
+
+ //
+ // find or create a orig face and return the index
+ //
+ index = FindOrigFace( f );
+
+ if( index == -1 )
+ return CreateOrigFace( f );
+ else if( index == -2 )
+ return -1;
+
+ return index;
+}
+
+/*
+==================
+EmitFace
+==================
+*/
+void EmitFace( face_t *f, qboolean onNode )
+{
+ dface_t *df;
+ int i;
+ int e;
+
+// void SubdivideFaceBySubdivSize( face_t *f ); // garymcthack
+// SubdivideFaceBySubdivSize( f );
+
+ // set initial output number
+ f->outputnumber = -1;
+
+ // degenerated
+ if( f->numpoints < 3 )
+ return;
+
+ // not a final face
+ if( f->merged || f->split[0] || f->split[1] )
+ return;
+
+ // don't emit NODRAW faces for runtime
+ if ( texinfo[f->texinfo].flags & SURF_NODRAW )
+ {
+ // keep NODRAW terrain surfaces though
+ if ( f->dispinfo == -1 )
+ return;
+ Warning("NODRAW on terrain surface!\n");
+ }
+
+ // save output number so leaffaces can use
+ f->outputnumber = numfaces;
+
+ //
+ // get the next available .bsp face slot
+ //
+ if (numfaces >= MAX_MAP_FACES)
+ Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
+ df = &dfaces[numfaces];
+
+ // Save the correlation between dfaces and faces -- since dfaces doesnt have worldcraft face id
+ dfaceids.AddToTail();
+ dfaceids[numfaces].hammerfaceid = f->originalface->id;
+
+ numfaces++;
+
+ //
+ // plane info - planenum is used by qlight, but not quake
+ //
+ df->planenum = f->planenum;
+ df->onNode = onNode;
+ df->side = f->planenum & 1;
+
+ //
+ // material info
+ //
+ df->texinfo = f->texinfo;
+ df->dispinfo = f->dispinfo;
+ df->smoothingGroups = f->smoothingGroups;
+
+ // save the original "side"/face data
+ df->origFace = FindOrCreateOrigFace( f );
+ df->surfaceFogVolumeID = -1;
+ dfacenodes[numfaces-1] = f->fogVolumeLeaf;
+ if ( f->fogVolumeLeaf )
+ {
+ Assert( f->fogVolumeLeaf->planenum == PLANENUM_LEAF );
+ }
+
+ //
+ // edge info
+ //
+ df->firstedge = numsurfedges;
+ df->numedges = f->numpoints;
+
+ // UNDONE: Nodraw faces have no winding - revisit to see if this is necessary
+ if ( f->w )
+ {
+ df->area = WindingArea( f->w );
+ }
+ else
+ {
+ df->area = 0;
+ }
+
+ df->firstPrimID = f->firstPrimID;
+ df->SetNumPrims( f->numPrims );
+ df->SetDynamicShadowsEnabled( f->originalface->m_bDynamicShadowsEnabled );
+
+ //
+ // save off points -- as edges
+ //
+ for( i = 0; i < f->numpoints; i++ )
+ {
+ //e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
+ e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
+
+ if (numsurfedges >= MAX_MAP_SURFEDGES)
+ Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
+ dsurfedges[numsurfedges] = e;
+ numsurfedges++;
+ }
+
+ // Create overlay face lists.
+ side_t *pSide = f->originalface;
+ if ( pSide )
+ {
+ int nOverlayCount = pSide->aOverlayIds.Count();
+ if ( nOverlayCount > 0 )
+ {
+ Overlay_AddFaceToLists( ( numfaces - 1 ), pSide );
+ }
+
+ nOverlayCount = pSide->aWaterOverlayIds.Count();
+ if ( nOverlayCount > 0 )
+ {
+ OverlayTransition_AddFaceToLists( ( numfaces - 1 ), pSide );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Emit all of the faces stored at the leaves (faces from detail brushes)
+//-----------------------------------------------------------------------------
+void EmitLeafFaces( face_t *pLeafFaceList )
+{
+ face_t *f = pLeafFaceList;
+ while ( f )
+ {
+ EmitFace( f, false );
+ f = f->next;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Free the list of faces stored at the leaves
+//-----------------------------------------------------------------------------
+void FreeLeafFaces( face_t *pLeafFaceList )
+{
+ int count = 0;
+ face_t *f, *next;
+
+ f = pLeafFaceList;
+
+ while ( f )
+ {
+ next = f->next;
+ FreeFace( f );
+ f = next;
+ count++;
+ }
+}
+
+/*
+============
+EmitDrawingNode_r
+============
+*/
+int EmitDrawNode_r (node_t *node)
+{
+ dnode_t *n;
+ face_t *f;
+ int i;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ EmitLeaf (node);
+ return -numleafs;
+ }
+
+ // emit a node
+ if (numnodes == MAX_MAP_NODES)
+ Error ("MAX_MAP_NODES");
+ node->diskId = numnodes;
+
+ n = &dnodes[numnodes];
+ numnodes++;
+
+ VECTOR_COPY (node->mins, n->mins);
+ VECTOR_COPY (node->maxs, n->maxs);
+
+ if (node->planenum & 1)
+ Error ("WriteDrawNodes_r: odd planenum");
+ n->planenum = node->planenum;
+ n->firstface = numfaces;
+ n->area = node->area;
+
+ if (!node->faces)
+ c_nofaces++;
+ else
+ c_facenodes++;
+
+ for (f=node->faces ; f ; f=f->next)
+ EmitFace (f, true);
+
+ n->numfaces = numfaces - n->firstface;
+
+
+ //
+ // recursively output the other nodes
+ //
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i]->planenum == PLANENUM_LEAF)
+ {
+ n->children[i] = -(numleafs + 1);
+ EmitLeaf (node->children[i]);
+ }
+ else
+ {
+ n->children[i] = numnodes;
+ EmitDrawNode_r (node->children[i]);
+ }
+ }
+
+ return n - dnodes;
+}
+
+
+//=========================================================
+
+// This will generate a scratchpad file with the level's geometry in it and the noshadow faces drawn red.
+// #define SCRATCHPAD_NO_SHADOW_FACES
+#if defined( SCRATCHPAD_NO_SHADOW_FACES )
+ #include "scratchpad_helpers.h"
+ IScratchPad3D *g_pPad;
+#endif
+
+
+void MarkNoShadowFaces()
+{
+#if defined( SCRATCHPAD_NO_SHADOW_FACES )
+ g_pPad = ScratchPad3D_Create();
+ ScratchPad_DrawWorld( g_pPad, false, CSPColor(1,1,1,0.3) );
+
+ for ( int iFace=0; iFace < numfaces; iFace++ )
+ {
+ dface_t *pFace = &dfaces[iFace];
+
+ if ( !pFace->AreDynamicShadowsEnabled() )
+ {
+ ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(1,0,0) );
+ ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(-1,0,0) );
+ ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(0,1,0) );
+ }
+ }
+ g_pPad->Release();
+#endif
+}
+
+struct texinfomap_t
+{
+ int refCount;
+ int outputIndex;
+};
+struct texdatamap_t
+{
+ int refCount;
+ int outputIndex;
+};
+
+// Find the best used texinfo to remap this brush side
+int FindMatchingBrushSideTexinfo( int sideIndex, const texinfomap_t *pMap )
+{
+ dbrushside_t &side = dbrushsides[sideIndex];
+ // find one with the same flags & surfaceprops (even if the texture name is different)
+ int sideTexFlags = texinfo[side.texinfo].flags;
+ int sideTexData = texinfo[side.texinfo].texdata;
+ int sideSurfaceProp = g_SurfaceProperties[sideTexData];
+ for ( int j = 0; j < texinfo.Count(); j++ )
+ {
+ if ( pMap[j].refCount > 0 &&
+ texinfo[j].flags == sideTexFlags &&
+ g_SurfaceProperties[texinfo[j].texdata] == sideSurfaceProp )
+ {
+ // found one
+ return j;
+ }
+ }
+
+ // can't find a better match
+ return side.texinfo;
+}
+
+// Remove all unused texinfos and rebuild array
+void ComapctTexinfoArray( texinfomap_t *pMap )
+{
+ CUtlVector<texinfo_t> old;
+ old.CopyArray( texinfo.Base(), texinfo.Count() );
+ texinfo.RemoveAll();
+ int firstSky = -1;
+ int first2DSky = -1;
+ for ( int i = 0; i < old.Count(); i++ )
+ {
+ if ( !pMap[i].refCount )
+ {
+ pMap[i].outputIndex = -1;
+ continue;
+ }
+ // only add one sky texinfo + one 2D sky texinfo
+ if ( old[i].flags & SURF_SKY2D )
+ {
+ if ( first2DSky < 0 )
+ {
+ first2DSky = texinfo.AddToTail( old[i] );
+ }
+ pMap[i].outputIndex = first2DSky;
+ continue;
+ }
+ if ( old[i].flags & SURF_SKY )
+ {
+ if ( firstSky < 0 )
+ {
+ firstSky = texinfo.AddToTail( old[i] );
+ }
+ pMap[i].outputIndex = firstSky;
+ continue;
+ }
+ pMap[i].outputIndex = texinfo.AddToTail( old[i] );
+ }
+}
+
+void CompactTexdataArray( texdatamap_t *pMap )
+{
+ CUtlVector<char> oldStringData;
+ oldStringData.CopyArray( g_TexDataStringData.Base(), g_TexDataStringData.Count() );
+ g_TexDataStringData.RemoveAll();
+ CUtlVector<int> oldStringTable;
+ oldStringTable.CopyArray( g_TexDataStringTable.Base(), g_TexDataStringTable.Count() );
+ g_TexDataStringTable.RemoveAll();
+ CUtlVector<dtexdata_t> oldTexData;
+ oldTexData.CopyArray( dtexdata, numtexdata );
+ // clear current table and rebuild
+ numtexdata = 0;
+ for ( int i = 0; i < oldTexData.Count(); i++ )
+ {
+ // unreferenced, note in map and skip
+ if ( !pMap[i].refCount )
+ {
+ pMap[i].outputIndex = -1;
+ continue;
+ }
+ pMap[i].outputIndex = numtexdata;
+
+ // get old string and re-add to table
+ const char *pString = &oldStringData[oldStringTable[oldTexData[i].nameStringTableID]];
+ int nameIndex = TexDataStringTable_AddOrFindString( pString );
+ // copy old texdata and fixup with new name in compacted table
+ dtexdata[numtexdata] = oldTexData[i];
+ dtexdata[numtexdata].nameStringTableID = nameIndex;
+ numtexdata++;
+ }
+}
+
+void CompactTexinfos()
+{
+ Msg("Compacting texture/material tables...\n");
+ texinfomap_t *texinfoMap = new texinfomap_t[texinfo.Count()];
+ texdatamap_t *texdataMap = new texdatamap_t[numtexdata];
+ memset( texinfoMap, 0, sizeof(texinfoMap[0])*texinfo.Count() );
+ memset( texdataMap, 0, sizeof(texdataMap[0])*numtexdata );
+ int i;
+ // get texinfos referenced by faces
+ for ( i = 0; i < numfaces; i++ )
+ {
+ texinfoMap[dfaces[i].texinfo].refCount++;
+ }
+ // get texinfos referenced by brush sides
+ for ( i = 0; i < numbrushsides; i++ )
+ {
+ // not referenced by any visible geometry
+ Assert( dbrushsides[i].texinfo >= 0 );
+ if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
+ {
+ dbrushsides[i].texinfo = FindMatchingBrushSideTexinfo( i, texinfoMap );
+ // didn't find anything suitable, go ahead and reference it
+ if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
+ {
+ texinfoMap[dbrushsides[i].texinfo].refCount++;
+ }
+ }
+ }
+ // get texinfos referenced by overlays
+ for ( i = 0; i < g_nOverlayCount; i++ )
+ {
+ texinfoMap[g_Overlays[i].nTexInfo].refCount++;
+ }
+ for ( i = 0; i < numleafwaterdata; i++ )
+ {
+ if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
+ {
+ texinfoMap[dleafwaterdata[i].surfaceTexInfoID].refCount++;
+ }
+ }
+ for ( i = 0; i < *pNumworldlights; i++ )
+ {
+ if ( dworldlights[i].texinfo >= 0 )
+ {
+ texinfoMap[dworldlights[i].texinfo].refCount++;
+ }
+ }
+ for ( i = 0; i < g_nWaterOverlayCount; i++ )
+ {
+ if ( g_WaterOverlays[i].nTexInfo >= 0 )
+ {
+ texinfoMap[g_WaterOverlays[i].nTexInfo].refCount++;
+ }
+ }
+ // reference all used texdatas
+ for ( i = 0; i < texinfo.Count(); i++ )
+ {
+ if ( texinfoMap[i].refCount > 0 )
+ {
+ texdataMap[texinfo[i].texdata].refCount++;
+ }
+ }
+
+ int oldCount = texinfo.Count();
+ int oldTexdataCount = numtexdata;
+ int oldTexdataString = g_TexDataStringData.Count();
+ ComapctTexinfoArray( texinfoMap );
+ CompactTexdataArray( texdataMap );
+ for ( i = 0; i < texinfo.Count(); i++ )
+ {
+ int mapIndex = texdataMap[texinfo[i].texdata].outputIndex;
+ Assert( mapIndex >= 0 );
+ texinfo[i].texdata = mapIndex;
+ //const char *pName = TexDataStringTable_GetString( dtexdata[texinfo[i].texdata].nameStringTableID );
+ }
+ // remap texinfos on faces
+ for ( i = 0; i < numfaces; i++ )
+ {
+ Assert( texinfoMap[dfaces[i].texinfo].outputIndex >= 0 );
+ dfaces[i].texinfo = texinfoMap[dfaces[i].texinfo].outputIndex;
+ }
+ // remap texinfos on brushsides
+ for ( i = 0; i < numbrushsides; i++ )
+ {
+ Assert( texinfoMap[dbrushsides[i].texinfo].outputIndex >= 0 );
+ dbrushsides[i].texinfo = texinfoMap[dbrushsides[i].texinfo].outputIndex;
+ }
+ // remap texinfos on overlays
+ for ( i = 0; i < g_nOverlayCount; i++ )
+ {
+ g_Overlays[i].nTexInfo = texinfoMap[g_Overlays[i].nTexInfo].outputIndex;
+ }
+ // remap leaf water data
+ for ( i = 0; i < numleafwaterdata; i++ )
+ {
+ if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
+ {
+ dleafwaterdata[i].surfaceTexInfoID = texinfoMap[dleafwaterdata[i].surfaceTexInfoID].outputIndex;
+ }
+ }
+ // remap world lights
+ for ( i = 0; i < *pNumworldlights; i++ )
+ {
+ if ( dworldlights[i].texinfo >= 0 )
+ {
+ dworldlights[i].texinfo = texinfoMap[dworldlights[i].texinfo].outputIndex;
+ }
+ }
+ // remap water overlays
+ for ( i = 0; i < g_nWaterOverlayCount; i++ )
+ {
+ if ( g_WaterOverlays[i].nTexInfo >= 0 )
+ {
+ g_WaterOverlays[i].nTexInfo = texinfoMap[g_WaterOverlays[i].nTexInfo].outputIndex;
+ }
+ }
+
+ Msg("Reduced %d texinfos to %d\n", oldCount, texinfo.Count() );
+ Msg("Reduced %d texdatas to %d (%d bytes to %d)\n", oldTexdataCount, numtexdata, oldTexdataString, g_TexDataStringData.Count() );
+
+ delete[] texinfoMap;
+ delete[] texdataMap;
+}
+
+/*
+============
+WriteBSP
+============
+*/
+void WriteBSP (node_t *headnode, face_t *pLeafFaceList )
+{
+ int i;
+ int oldfaces;
+ int oldorigfaces;
+
+ c_nofaces = 0;
+ c_facenodes = 0;
+
+ qprintf ("--- WriteBSP ---\n");
+
+ oldfaces = numfaces;
+ oldorigfaces = numorigfaces;
+
+ GetEdge2_InitOptimizedList();
+ EmitLeafFaces( pLeafFaceList );
+ dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
+
+ // Only emit area portals for the main world.
+ if( nummodels == 0 )
+ {
+ EmitAreaPortals (headnode);
+ }
+
+ //
+ // add all displacement faces for the particular model
+ //
+ for( i = 0; i < nummapdispinfo; i++ )
+ {
+ int entityIndex = GetDispInfoEntityNum( &mapdispinfo[i] );
+ if( entityIndex == entity_num )
+ {
+ EmitFaceVertexes( NULL, &mapdispinfo[i].face );
+ EmitFace( &mapdispinfo[i].face, FALSE );
+ }
+ }
+
+ EmitWaterVolumesForBSP( &dmodels[nummodels], headnode );
+ qprintf ("%5i nodes with faces\n", c_facenodes);
+ qprintf ("%5i nodes without faces\n", c_nofaces);
+ qprintf ("%5i faces\n", numfaces-oldfaces);
+ qprintf( "%5i original faces\n", numorigfaces-oldorigfaces );
+}
+
+
+
+//===========================================================
+
+/*
+============
+SetModelNumbers
+============
+*/
+void SetModelNumbers (void)
+{
+ int i;
+ int models;
+ char value[10];
+
+ models = 1;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ if (!entities[i].numbrushes)
+ continue;
+
+ if ( !IsFuncOccluder(i) )
+ {
+ sprintf (value, "*%i", models);
+ models++;
+ }
+ else
+ {
+ sprintf (value, "");
+ }
+ SetKeyValue (&entities[i], "model", value);
+ }
+}
+
+
+/*
+============
+SetLightStyles
+============
+*/
+#define MAX_SWITCHED_LIGHTS 32
+void SetLightStyles (void)
+{
+ int stylenum;
+ char *t;
+ entity_t *e;
+ int i, j;
+ char value[10];
+ char lighttargets[MAX_SWITCHED_LIGHTS][64];
+
+
+ // any light that is controlled (has a targetname)
+ // must have a unique style number generated for it
+
+ stylenum = 0;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ e = &entities[i];
+
+ t = ValueForKey (e, "classname");
+ if (Q_strncasecmp (t, "light", 5))
+ continue;
+
+ // This is not true for dynamic lights
+ if (!Q_strcasecmp (t, "light_dynamic"))
+ continue;
+
+ t = ValueForKey (e, "targetname");
+ if (!t[0])
+ continue;
+
+ // find this targetname
+ for (j=0 ; j<stylenum ; j++)
+ if (!strcmp (lighttargets[j], t))
+ break;
+ if (j == stylenum)
+ {
+ if (stylenum == MAX_SWITCHED_LIGHTS)
+ Error ("Too many switched lights (error at light %s), max = %d", t, MAX_SWITCHED_LIGHTS);
+ strcpy (lighttargets[j], t);
+ stylenum++;
+ }
+ sprintf (value, "%i", 32 + j);
+ char *pCurrentStyle = ValueForKey( e, "style" );
+ // the designer has set a default lightstyle as well as making the light switchable
+ if ( pCurrentStyle )
+ {
+ int oldStyle = atoi(pCurrentStyle);
+ if ( oldStyle != 0 )
+ {
+ // save off the default style so the game code can make a switchable copy of it
+ SetKeyValue( e, "defaultstyle", pCurrentStyle );
+ }
+ }
+ SetKeyValue (e, "style", value);
+ }
+
+}
+
+/*
+============
+EmitBrushes
+============
+*/
+void EmitBrushes (void)
+{
+ int i, j, bnum, s, x;
+ dbrush_t *db;
+ mapbrush_t *b;
+ dbrushside_t *cp;
+ Vector normal;
+ vec_t dist;
+ int planenum;
+
+ numbrushsides = 0;
+ numbrushes = g_MainMap->nummapbrushes;
+
+ for (bnum=0 ; bnum<g_MainMap->nummapbrushes ; bnum++)
+ {
+ b = &g_MainMap->mapbrushes[bnum];
+ db = &dbrushes[bnum];
+
+ db->contents = b->contents;
+ db->firstside = numbrushsides;
+ db->numsides = b->numsides;
+ for (j=0 ; j<b->numsides ; j++)
+ {
+ if (numbrushsides == MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+ cp = &dbrushsides[numbrushsides];
+ numbrushsides++;
+ cp->planenum = b->original_sides[j].planenum;
+ cp->texinfo = b->original_sides[j].texinfo;
+ if ( cp->texinfo == -1 )
+ {
+ cp->texinfo = g_MainMap->g_ClipTexinfo;
+ }
+ cp->bevel = b->original_sides[j].bevel;
+ }
+
+ // add any axis planes not contained in the brush to bevel off corners
+ for (x=0 ; x<3 ; x++)
+ for (s=-1 ; s<=1 ; s+=2)
+ {
+ // add the plane
+ VectorCopy (vec3_origin, normal);
+ normal[x] = s;
+ if (s == -1)
+ dist = -b->mins[x];
+ else
+ dist = b->maxs[x];
+ planenum = g_MainMap->FindFloatPlane (normal, dist);
+ for (i=0 ; i<b->numsides ; i++)
+ if (b->original_sides[i].planenum == planenum)
+ break;
+ if (i == b->numsides)
+ {
+ if (numbrushsides >= MAX_MAP_BRUSHSIDES)
+ Error ("MAX_MAP_BRUSHSIDES");
+
+ dbrushsides[numbrushsides].planenum = planenum;
+ dbrushsides[numbrushsides].texinfo =
+ dbrushsides[numbrushsides-1].texinfo;
+ numbrushsides++;
+ db->numsides++;
+ }
+ }
+ }
+}
+
+
+
+/*
+==================
+BeginBSPFile
+==================
+*/
+void BeginBSPFile (void)
+{
+ // these values may actually be initialized
+ // if the file existed when loaded, so clear them explicitly
+ nummodels = 0;
+ numfaces = 0;
+ numnodes = 0;
+ numbrushsides = 0;
+ numvertexes = 0;
+ numleaffaces = 0;
+ numleafbrushes = 0;
+ numsurfedges = 0;
+
+ // edge 0 is not used, because 0 can't be negated
+ numedges = 1;
+
+ // leave vertex 0 as an error
+ numvertexes = 1;
+
+ // leave leaf 0 as an error
+ numleafs = 1;
+ dleafs[0].contents = CONTENTS_SOLID;
+
+ // BUGBUG: This doesn't work!
+#if 0
+ // make a default empty leaf for the tracing code
+ memset( &dleafs[1], 0, sizeof(dleafs[1]) );
+ dleafs[1].contents = CONTENTS_EMPTY;
+#endif
+}
+
+// We can't calculate this properly until vvis (since we need vis to do this), so we set
+// to zero everywhere by default.
+static void ClearDistToClosestWater( void )
+{
+ int i;
+ for( i = 0; i < numleafs; i++ )
+ {
+ g_LeafMinDistToWater[i] = 0;
+ }
+}
+
+
+void DiscoverMacroTextures()
+{
+ CUtlDict<int,int> tempDict;
+
+ g_FaceMacroTextureInfos.SetSize( numfaces );
+ for ( int iFace=0; iFace < numfaces; iFace++ )
+ {
+ texinfo_t *pTexInfo = &texinfo[dfaces[iFace].texinfo];
+ if ( pTexInfo->texdata < 0 )
+ continue;
+
+ dtexdata_t *pTexData = &dtexdata[pTexInfo->texdata];
+ const char *pMaterialName = &g_TexDataStringData[ g_TexDataStringTable[pTexData->nameStringTableID] ];
+
+ MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
+
+ const char *pMacroTextureName = GetMaterialVar( hMaterial, "$macro_texture" );
+ if ( pMacroTextureName )
+ {
+ if ( tempDict.Find( pMacroTextureName ) == tempDict.InvalidIndex() )
+ {
+ Msg( "-- DiscoverMacroTextures: %s\n", pMacroTextureName );
+ tempDict.Insert( pMacroTextureName, 0 );
+ }
+
+ int stringID = TexDataStringTable_AddOrFindString( pMacroTextureName );
+ g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = (unsigned short)stringID;
+ }
+ else
+ {
+ g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = 0xFFFF;
+ }
+ }
+}
+
+
+// Make sure that we have a water lod control entity if we have water in the map.
+void EnsurePresenceOfWaterLODControlEntity( void )
+{
+ extern bool g_bHasWater;
+ if( !g_bHasWater )
+ {
+ // Don't bother if there isn't any water in the map.
+ return;
+ }
+ for( int i=0; i < num_entities; i++ )
+ {
+ entity_t *e = &entities[i];
+
+ const char *pClassName = ValueForKey( e, "classname" );
+ if( !Q_stricmp( pClassName, "water_lod_control" ) )
+ {
+ // Found one!!!!
+ return;
+ }
+ }
+
+ // None found, add one.
+ Warning( "Water found with no water_lod_control entity, creating a default one.\n" );
+
+ entity_t *mapent = &entities[num_entities];
+ num_entities++;
+ memset(mapent, 0, sizeof(*mapent));
+ mapent->firstbrush = g_MainMap->nummapbrushes;
+ mapent->numbrushes = 0;
+
+ SetKeyValue( mapent, "classname", "water_lod_control" );
+ SetKeyValue( mapent, "cheapwaterstartdistance", "1000" );
+ SetKeyValue( mapent, "cheapwaterenddistance", "2000" );
+}
+
+
+/*
+============
+EndBSPFile
+============
+*/
+void EndBSPFile (void)
+{
+ // Mark noshadow faces.
+ MarkNoShadowFaces();
+
+ EmitBrushes ();
+ EmitPlanes ();
+
+ // stick flat normals at the verts
+ SaveVertexNormals();
+
+ // Figure out lightmap extents for all faces.
+ UpdateAllFaceLightmapExtents();
+
+ // Generate geometry and lightmap alpha for displacements.
+ EmitDispLMAlphaAndNeighbors();
+
+ // Emit overlay data.
+ Overlay_EmitOverlayFaces();
+ OverlayTransition_EmitOverlayFaces();
+
+ // phys collision needs dispinfo to operate (needs to generate phys collision for displacement surfs)
+ EmitPhysCollision();
+
+ // We can't calculate this properly until vvis (since we need vis to do this), so we set
+ // to zero everywhere by default.
+ ClearDistToClosestWater();
+
+ // Emit static props found in the .vmf file
+ EmitStaticProps();
+
+ // Place detail props found in .vmf and based on material properties
+ EmitDetailObjects();
+
+ // Compute bounds after creating disp info because we need to reference it
+ ComputeBoundsNoSkybox();
+
+ // Make sure that we have a water lod control eneity if we have water in the map.
+ EnsurePresenceOfWaterLODControlEntity();
+
+ // Doing this here because stuff about may filter out entities
+ UnparseEntities ();
+
+ // remove unused texinfos
+ CompactTexinfos();
+
+ // Figure out which faces want macro textures.
+ DiscoverMacroTextures();
+
+ char targetPath[1024];
+ GetPlatformMapPath( source, targetPath, g_nDXLevel, 1024 );
+ Msg ("Writing %s\n", targetPath);
+ WriteBSPFile (targetPath);
+}
+
+
+/*
+==================
+BeginModel
+==================
+*/
+int firstmodleaf;
+void BeginModel (void)
+{
+ dmodel_t *mod;
+ int start, end;
+ mapbrush_t *b;
+ int j;
+ entity_t *e;
+ Vector mins, maxs;
+
+ if (nummodels == MAX_MAP_MODELS)
+ Error ("Too many brush models in map, max = %d", MAX_MAP_MODELS);
+ mod = &dmodels[nummodels];
+
+ mod->firstface = numfaces;
+
+ firstmodleaf = numleafs;
+ firstmodeledge = numedges;
+ firstmodelface = numfaces;
+
+ //
+ // bound the brushes
+ //
+ e = &entities[entity_num];
+
+ start = e->firstbrush;
+ end = start + e->numbrushes;
+ ClearBounds (mins, maxs);
+
+ for (j=start ; j<end ; j++)
+ {
+ b = &g_MainMap->mapbrushes[j];
+ if (!b->numsides)
+ continue; // not a real brush (origin brush)
+ AddPointToBounds (b->mins, mins, maxs);
+ AddPointToBounds (b->maxs, mins, maxs);
+ }
+
+ VectorCopy (mins, mod->mins);
+ VectorCopy (maxs, mod->maxs);
+}
+
+
+/*
+==================
+EndModel
+==================
+*/
+void EndModel (void)
+{
+ dmodel_t *mod;
+
+ mod = &dmodels[nummodels];
+
+ mod->numfaces = numfaces - mod->firstface;
+
+ nummodels++;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// figure out which leaf a point is in
+//-----------------------------------------------------------------------------
+static int PointLeafnum_r (const Vector& p, int num)
+{
+ float d;
+ while (num >= 0)
+ {
+ dnode_t* node = dnodes + num;
+ dplane_t* plane = dplanes + node->planenum;
+
+ if (plane->type < 3)
+ d = p[plane->type] - plane->dist;
+ else
+ d = DotProduct (plane->normal, p) - plane->dist;
+ if (d < 0)
+ num = node->children[1];
+ else
+ num = node->children[0];
+ }
+
+ return -1 - num;
+}
+
+int PointLeafnum ( dmodel_t* pModel, const Vector& p )
+{
+ return PointLeafnum_r (p, pModel->headnode);
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a noew to the bounding box
+//-----------------------------------------------------------------------------
+static void AddNodeToBounds(int node, CUtlVector<int>& skipAreas, Vector& mins, Vector& maxs)
+{
+ // not a leaf
+ if (node >= 0)
+ {
+ AddNodeToBounds( dnodes[node].children[0], skipAreas, mins, maxs );
+ AddNodeToBounds( dnodes[node].children[1], skipAreas, mins, maxs );
+ }
+ else
+ {
+ int leaf = - 1 - node;
+
+ // Don't bother with solid leaves
+ if (dleafs[leaf].contents & CONTENTS_SOLID)
+ return;
+
+ // Skip 3D skybox
+ int i;
+ for ( i = skipAreas.Count(); --i >= 0; )
+ {
+ if (dleafs[leaf].area == skipAreas[i])
+ return;
+ }
+
+ unsigned int firstface = dleafs[leaf].firstleafface;
+ for ( i = 0; i < dleafs[leaf].numleaffaces; ++i )
+ {
+ unsigned int face = dleaffaces[ firstface + i ];
+
+ // Skip skyboxes + nodraw
+ texinfo_t& tex = texinfo[dfaces[face].texinfo];
+ if (tex.flags & (SURF_SKY | SURF_NODRAW))
+ continue;
+
+ unsigned int firstedge = dfaces[face].firstedge;
+ Assert( firstedge >= 0 );
+
+ for (int j = 0; j < dfaces[face].numedges; ++j)
+ {
+ Assert( firstedge+j < numsurfedges );
+ int edge = abs(dsurfedges[firstedge+j]);
+ dedge_t* pEdge = &dedges[edge];
+ Assert( pEdge->v[0] >= 0 );
+ Assert( pEdge->v[1] >= 0 );
+ AddPointToBounds (dvertexes[pEdge->v[0]].point, mins, maxs);
+ AddPointToBounds (dvertexes[pEdge->v[1]].point, mins, maxs);
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Check to see if a displacement lives in any leaves that are not
+// in the 3d skybox
+//-----------------------------------------------------------------------------
+bool IsBoxInsideWorld( int node, CUtlVector<int> &skipAreas, const Vector &vecMins, const Vector &vecMaxs )
+{
+ while( 1 )
+ {
+ // leaf
+ if (node < 0)
+ {
+ // get the leaf
+ int leaf = - 1 - node;
+
+ // Don't bother with solid leaves
+ if (dleafs[leaf].contents & CONTENTS_SOLID)
+ return false;
+
+ // Skip 3D skybox
+ int i;
+ for ( i = skipAreas.Count(); --i >= 0; )
+ {
+ if ( dleafs[leaf].area == skipAreas[i] )
+ return false;
+ }
+
+ return true;
+ }
+
+ //
+ // get displacement bounding box position relative to the node plane
+ //
+ dnode_t *pNode = &dnodes[ node ];
+ dplane_t *pPlane = &dplanes[ pNode->planenum ];
+
+ int sideResult = BrushBspBoxOnPlaneSide( vecMins, vecMaxs, pPlane );
+
+ // front side
+ if( sideResult == 1 )
+ {
+ node = pNode->children[0];
+ }
+ // back side
+ else if( sideResult == 2 )
+ {
+ node = pNode->children[1];
+ }
+ //split
+ else
+ {
+ if ( IsBoxInsideWorld( pNode->children[0], skipAreas, vecMins, vecMaxs ) )
+ return true;
+
+ node = pNode->children[1];
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds the displacement surfaces in the world to the bounds
+//-----------------------------------------------------------------------------
+void AddDispsToBounds( int nHeadNode, CUtlVector<int>& skipAreas, Vector &vecMins, Vector &vecMaxs )
+{
+ Vector vecDispMins, vecDispMaxs;
+
+ // first determine how many displacement surfaces there will be per leaf
+ int i;
+ for ( i = 0; i < g_dispinfo.Count(); ++i )
+ {
+ ComputeDispInfoBounds( i, vecDispMins, vecDispMaxs );
+ if ( IsBoxInsideWorld( nHeadNode, skipAreas, vecDispMins, vecDispMaxs ) )
+ {
+ AddPointToBounds( vecDispMins, vecMins, vecMaxs );
+ AddPointToBounds( vecDispMaxs, vecMins, vecMaxs );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
+//-----------------------------------------------------------------------------
+void ComputeBoundsNoSkybox( )
+{
+ // Iterate over all world leaves, skip those which are part of skybox
+ Vector mins, maxs;
+ ClearBounds (mins, maxs);
+ AddNodeToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
+ AddDispsToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
+
+ // Add the bounds to the worldspawn data
+ for (int i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!strcmp(pEntity, "worldspawn"))
+ {
+ char string[32];
+ sprintf (string, "%i %i %i", (int)mins[0], (int)mins[1], (int)mins[2]);
+ SetKeyValue (&entities[i], "world_mins", string);
+ sprintf (string, "%i %i %i", (int)maxs[0], (int)maxs[1], (int)maxs[2]);
+ SetKeyValue (&entities[i], "world_maxs", string);
+ break;
+ }
+ }
+}
+
+
diff --git a/mp/src/utils/vbsp/writebsp.h b/mp/src/utils/vbsp/writebsp.h new file mode 100644 index 00000000..e1ad084e --- /dev/null +++ b/mp/src/utils/vbsp/writebsp.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WRITEBSP_H
+#define WRITEBSP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "bspfile.h"
+#include "utlmap.h"
+
+struct node_t;
+
+//-----------------------------------------------------------------------------
+// Emits occluder faces
+//-----------------------------------------------------------------------------
+void EmitOccluderFaces (node_t *node);
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Free the list of faces stored at the leaves
+//-----------------------------------------------------------------------------
+void FreeLeafFaces( face_t *pLeafFaceList );
+
+//-----------------------------------------------------------------------------
+// Purpose: Make sure that we have a water lod control entity if we have water in the map.
+//-----------------------------------------------------------------------------
+void EnsurePresenceOfWaterLODControlEntity( void );
+
+#endif // WRITEBSP_H
diff --git a/mp/src/utils/vice/vice-2010.vcxproj b/mp/src/utils/vice/vice-2010.vcxproj new file mode 100644 index 00000000..12e10a33 --- /dev/null +++ b/mp/src/utils/vice/vice-2010.vcxproj @@ -0,0 +1,247 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vice</ProjectName>
+ <ProjectGuid>{03F753C0-8BA5-FF2B-D7D2-EE230B4683B1}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vice</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vice</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vice.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vice;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vice.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vice.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vice.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vice;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vice.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vice.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\IceKey.H" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\cmdlib.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="..\..\public\filesystem_init.cpp" />
+ <ClCompile Include="..\common\filesystem_tools.cpp" />
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="vice.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vice/vice-2010.vcxproj.filters b/mp/src/utils/vice/vice-2010.vcxproj.filters new file mode 100644 index 00000000..82155f11 --- /dev/null +++ b/mp/src/utils/vice/vice-2010.vcxproj.filters @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\IceKey.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\cmdlib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_init.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\filesystem_tools.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vice.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vice/vice.cpp b/mp/src/utils/vice/vice.cpp new file mode 100644 index 00000000..0ed9c32e --- /dev/null +++ b/mp/src/utils/vice/vice.cpp @@ -0,0 +1,277 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vice.cpp : Defines the entry point for the console application.
+//
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <string.h>
+#include "tier1/strtools.h"
+#include <sys/stat.h>
+#include "conio.h"
+#include <direct.h>
+#include <io.h>
+#include "UtlBuffer.h"
+#include "tier0/dbg.h"
+#include "cmdlib.h"
+#include "tier0/icommandline.h"
+#include "windows.h"
+
+#include "mathlib/IceKey.h"
+#include <filesystem_tools.h>
+
+#define FF_TRYAGAIN 1
+#define FF_DONTPROCESS 2
+
+#undef GetCurrentDirectory
+
+static bool g_NoPause = false;
+static bool g_Quiet = false;
+static bool g_Encrypt = false;
+static bool g_Decrypt = false;
+static char g_ICEKey[16];
+static char g_Extension[16];
+
+static void Pause( void )
+{
+ if( !g_NoPause )
+ {
+ printf( "Hit a key to continue\n" );
+ getch();
+ }
+}
+
+static void Exit(const char *msg)
+{
+ fprintf( stderr, msg );
+ Pause();
+ exit( -1 );
+}
+
+static void Usage( void )
+{
+ fprintf( stderr, "Usage: vice [-quiet] [-nopause] [-encrypt key] [-decrypt key] [-newext name] file file2 . . .\n" );
+ fprintf( stderr, "-quiet : don't print anything out, don't pause for input\n" );
+ fprintf( stderr, "-nopause : don't pause for input\n" );
+ fprintf( stderr, "-encrypt : encrypt files with given key\n" );
+ fprintf( stderr, "-decrypt : decypt files with given key\n" );
+ fprintf( stderr, "-newext : new output file extension\n" );
+ Pause();
+ exit( -1 );
+}
+
+
+bool Process_File( char *pInputBaseName, int maxlen )
+{
+ Q_FixSlashes( pInputBaseName, '/' );
+ // Q_StripExtension( pInputBaseName, pInputBaseName, maxlen );
+
+ if( !g_Quiet )
+ {
+ printf( "input file: %s\n", pInputBaseName );
+ }
+
+ FileHandle_t f = g_pFullFileSystem->Open(pInputBaseName, "rb", "vice" );
+
+ if (!f)
+ Error("Could not open input file");
+
+ int fileSize = g_pFullFileSystem->Size(f);
+
+ unsigned char *buffer = (unsigned char*)_alloca(fileSize);
+
+ g_pFullFileSystem->Read(buffer, fileSize, f); // read into local buffer
+ g_pFullFileSystem->Close( f ); // close file after reading
+
+ IceKey ice( 0 ); // level 0 = 64bit key
+ ice.set( (unsigned char*) g_ICEKey ); // set key
+
+ int blockSize = ice.blockSize();
+
+ unsigned char *temp = (unsigned char *)_alloca( fileSize );
+ unsigned char *p1 = buffer;
+ unsigned char *p2 = temp;
+
+ // encrypt data in 8 byte blocks
+ int bytesLeft = fileSize;
+ while ( bytesLeft >= blockSize )
+ {
+ if ( g_Encrypt )
+ {
+ ice.encrypt( p1, p2 );
+ }
+ else if ( g_Decrypt )
+ {
+ ice.decrypt( p1, p2 );
+ }
+ else
+ {
+ memcpy( p2, p1, blockSize );
+ }
+
+ bytesLeft -= blockSize;
+ p1+=blockSize;
+ p2+=blockSize;
+ }
+
+ memcpy( p2, p1, bytesLeft );
+
+ Q_SetExtension( pInputBaseName, g_Extension, maxlen );
+
+ if( !g_Quiet )
+ {
+ printf( "output file: %s\n", pInputBaseName );
+ }
+
+ f = g_pFullFileSystem->Open(pInputBaseName, "wb", "vice" );
+
+ if (!f)
+ Exit("Could not open output file");
+
+ g_pFullFileSystem->Write( temp, fileSize, f ); // read into local buffer
+ g_pFullFileSystem->Close( f ); // close file after reading
+
+ return TRUE;
+}
+
+int main(int argc, char* argv[])
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+
+
+ if( argc < 2 )
+ {
+ Usage();
+ }
+ char *pInputBaseName = NULL;
+ int i = 1;
+ strcpy( g_Extension, ".dat" );
+ while( i < argc )
+ {
+ if( stricmp( argv[i], "-quiet" ) == 0 )
+ {
+ i++;
+ g_Quiet = true;
+ g_NoPause = true; // no point in pausing if we aren't going to print anything out.
+ }
+ if( stricmp( argv[i], "-nopause" ) == 0 )
+ {
+ i++;
+ g_NoPause = true;
+ }
+ if( stricmp( argv[i], "-encrypt" ) == 0 )
+ {
+ g_Encrypt = true;
+ i++;
+
+ if ( strlen( argv[i] ) != 8 )
+ {
+ Exit("Error - ICE key must be a 8 char text.\n");
+ }
+
+ Q_strncpy( g_ICEKey, argv[i], sizeof(g_ICEKey) );
+ i++;
+
+ }
+ if( stricmp( argv[i], "-decrypt" ) == 0 )
+ {
+ g_Decrypt = true;
+ i++;
+
+ if ( strlen( argv[i] ) != 8 )
+ {
+ Exit("Error - ICE key must be a 8 char text.\n");
+ }
+
+ Q_strncpy( g_ICEKey, argv[i], sizeof(g_ICEKey) );
+ i++;
+
+ }
+ if( stricmp( argv[i], "-newext" ) == 0 )
+ {
+ i++;
+
+ if ( strlen( argv[i] ) > 5 )
+ {
+ Exit("Error - extension must be smaller than 4 chars.\n");
+ }
+
+ Q_strncpy( g_Extension, argv[i], sizeof(g_Extension) );
+ i++;
+
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if ( i >= argc )
+ {
+ Exit("Error - missing files in commandline.\n");
+ }
+
+ CmdLib_InitFileSystem( argv[i] );
+
+ g_pFullFileSystem->GetCurrentDirectory( gamedir, sizeof(gamedir) );
+ g_pFullFileSystem->AddSearchPath( gamedir, "vice" );
+
+
+ Q_FixSlashes( gamedir, '/' );
+
+ for( ; i < argc; i++ )
+ {
+ pInputBaseName = argv[i];
+ int maxlen = Q_strlen( pInputBaseName ) + 1;
+
+
+ if ( strstr( pInputBaseName, "*.") )
+ {
+ char search[ MAX_PATH ];
+ char fname[ MAX_PATH ];
+ char ext[_MAX_EXT];
+
+ _splitpath( pInputBaseName, NULL, NULL, fname, ext ); //find extension wanted
+ fname[strlen(fname)-1] = 0; // remove *
+
+ sprintf( search, "%s\\*%s", gamedir, ext );
+
+ Q_FixSlashes( search, '/' );
+
+ WIN32_FIND_DATA wfd;
+ HANDLE hResult;
+ memset(&wfd, 0, sizeof(WIN32_FIND_DATA));
+
+ hResult = FindFirstFile( search, &wfd );
+
+ while ( hResult != INVALID_HANDLE_VALUE )
+ {
+ if ( !strnicmp( fname, wfd.cFileName, strlen(fname) ) )
+ {
+ if ( !Process_File( wfd.cFileName, sizeof( wfd.cFileName ) ) )
+ break;
+ }
+
+ if ( !FindNextFile( hResult, &wfd) )
+ break;
+
+ }
+
+ FindClose( hResult );
+ }
+ else
+ {
+ Process_File( pInputBaseName, maxlen );
+ }
+ }
+
+ Pause();
+ return 0;
+}
+
diff --git a/mp/src/utils/vmpi/ichannel.h b/mp/src/utils/vmpi/ichannel.h new file mode 100644 index 00000000..42466cfc --- /dev/null +++ b/mp/src/utils/vmpi/ichannel.h @@ -0,0 +1,49 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ICHANNEL_H
+#define ICHANNEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "tier1/utlvector.h"
+
+
+class IChannel
+{
+public:
+ // Note: this also releases any channels contained inside. So if you make a reliable
+ // channel that contains an unreliable channel and release the reliable one,
+ // it will automatically release the unreliable one it contains.
+ virtual void Release() = 0;
+
+ // Send data to the destination.
+ virtual bool Send( const void *pData, int len ) = 0;
+
+ // This version puts all the chunks into one packet and ships it off.
+ virtual bool SendChunks( void const * const *pChunks, const int *pChunkLengths, int nChunks ) = 0;
+
+ // Check for any packets coming in from the destination.
+ // Returns false if no packet was received.
+ //
+ // flTimeout can be used to make it wait for data.
+ //
+ // Note: this is most efficient if you keep the buffer around between calls so it only
+ // reallocates it when it needs more space.
+ virtual bool Recv( CUtlVector<unsigned char> &data, double flTimeout=0 ) = 0;
+
+ // Returns false if the connection has been broken.
+ virtual bool IsConnected() = 0;
+
+ // If IsConnected returns false, you can call this to find out why the socket got disconnected.
+ virtual void GetDisconnectReason( CUtlVector<char> &reason ) = 0;
+};
+
+
+#endif // ICHANNEL_H
diff --git a/mp/src/utils/vmpi/imysqlwrapper.h b/mp/src/utils/vmpi/imysqlwrapper.h new file mode 100644 index 00000000..f5a8dfa3 --- /dev/null +++ b/mp/src/utils/vmpi/imysqlwrapper.h @@ -0,0 +1,115 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MYSQL_WRAPPER_H
+#define MYSQL_WRAPPER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "utlvector.h"
+#include "interface.h"
+
+
+class IMySQLRowSet;
+
+
+class CColumnValue
+{
+public:
+
+ CColumnValue( IMySQLRowSet *pSQL, int iColumn );
+
+ const char* String();
+ long Int32();
+
+private:
+ IMySQLRowSet *m_pSQL;
+ int m_iColumn;
+};
+
+
+
+class IMySQLRowSet
+{
+public:
+ virtual void Release() = 0;
+
+ // Get the number of columns in the data returned from the last query (if it was a select statement).
+ virtual int NumFields() = 0;
+
+ // Get the name of each column returned by the last query.
+ virtual const char* GetFieldName( int iColumn ) = 0;
+
+ // Call this in a loop until it returns false to iterate over all rows the query returned.
+ virtual bool NextRow() = 0;
+
+ // You can call this to start iterating over the result set from the start again.
+ // Note: after calling this, you have to call NextRow() to actually get the first row's value ready.
+ virtual bool SeekToFirstRow() = 0;
+
+ virtual CColumnValue GetColumnValue( int iColumn ) = 0;
+ virtual CColumnValue GetColumnValue( const char *pColumnName ) = 0;
+
+ virtual const char* GetColumnValue_String( int iColumn ) = 0;
+ virtual long GetColumnValue_Int( int iColumn ) = 0;
+
+ // You can call this to get the index of a column for faster lookups with GetColumnValue( int ).
+ // Returns -1 if the column can't be found.
+ virtual int GetColumnIndex( const char *pColumnName ) = 0;
+};
+
+
+class IMySQL : public IMySQLRowSet
+{
+public:
+ virtual bool InitMySQL( const char *pDBName, const char *pHostName="", const char *pUserName="", const char *pPassword="" ) = 0;
+ virtual void Release() = 0;
+
+ // These execute SQL commands. They return 0 if the query was successful.
+ virtual int Execute( const char *pString ) = 0;
+
+ // This reads in all of the data in the last row set you queried with Execute and builds a separate
+ // copy. This is useful in some of the VMPI tools to have a thread repeatedly execute a slow query, then
+ // store off the results for the main thread to parse.
+ virtual IMySQLRowSet* DuplicateRowSet() = 0;
+
+ // If you just inserted rows into a table with an AUTO_INCREMENT column,
+ // then this returns the (unique) value of that column.
+ virtual unsigned long InsertID() = 0;
+
+ // Returns the last error message, if an error took place
+ virtual const char * GetLastError() = 0;
+};
+
+
+#define MYSQL_WRAPPER_VERSION_NAME "MySQLWrapper001"
+
+
+// ------------------------------------------------------------------------------------------------ //
+// Inlines.
+// ------------------------------------------------------------------------------------------------ //
+
+inline CColumnValue::CColumnValue( IMySQLRowSet *pSQL, int iColumn )
+{
+ m_pSQL = pSQL;
+ m_iColumn = iColumn;
+}
+
+inline const char* CColumnValue::String()
+{
+ return m_pSQL->GetColumnValue_String( m_iColumn );
+}
+
+inline long CColumnValue::Int32()
+{
+ return m_pSQL->GetColumnValue_Int( m_iColumn );
+}
+
+
+#endif // MYSQL_WRAPPER_H
diff --git a/mp/src/utils/vmpi/iphelpers.h b/mp/src/utils/vmpi/iphelpers.h new file mode 100644 index 00000000..570a0c74 --- /dev/null +++ b/mp/src/utils/vmpi/iphelpers.h @@ -0,0 +1,162 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#ifndef IPHELPERS_H
+#define IPHELPERS_H
+
+
+#include "ichannel.h"
+
+
+// Loops that poll sockets should Sleep for this amount of time between iterations
+// so they don't hog all the CPU.
+#define LOOP_POLL_INTERVAL 5
+
+
+// Useful for putting the arguments into a printf statement.
+#define EXPAND_ADDR( x ) (x).ip[0], (x).ip[1], (x).ip[2], (x).ip[3], (x).port
+
+
+// This is a simple wrapper layer for UDP sockets.
+class CIPAddr
+{
+public:
+ CIPAddr();
+ CIPAddr( const int inputIP[4], const int inputPort );
+ CIPAddr( int ip0, int ip1, int ip2, int ip3, int ipPort );
+
+ void Init( int ip0, int ip1, int ip2, int ip3, int ipPort );
+ bool operator==( const CIPAddr &o ) const;
+ bool operator!=( const CIPAddr &o ) const;
+
+ // Setup to send to the local machine on the specified port.
+ void SetupLocal( int inPort );
+
+public:
+
+ unsigned char ip[4];
+ unsigned short port;
+};
+
+
+
+// The chunk walker provides an easy way to copy data out of the chunks as though it were a
+// single contiguous chunk of memory.s
+class CChunkWalker
+{
+public:
+ CChunkWalker( void const * const *pChunks, const int *pChunkLengths, int nChunks );
+
+ int GetTotalLength() const;
+ void CopyTo( void *pOut, int nBytes );
+
+private:
+
+ void const * const *m_pChunks;
+ const int *m_pChunkLengths;
+ int m_nChunks;
+
+ int m_iCurChunk;
+ int m_iCurChunkPos;
+
+ int m_TotalLength;
+};
+
+
+// This class makes loop that wait on something look nicer. ALL loops using this class
+// should follow this pattern, or they can wind up with unforeseen delays that add a whole
+// lot of lag.
+//
+// CWaitTimer waitTimer( 5.0 );
+// while ( 1 )
+// {
+// do your thing here like Recv() from a socket.
+//
+// if ( waitTimer.ShouldKeepWaiting() )
+// Sleep() for some time interval like 5ms so you don't hog the CPU
+// else
+// BREAK HERE
+// }
+class CWaitTimer
+{
+public:
+ CWaitTimer( double flSeconds );
+
+ bool ShouldKeepWaiting();
+
+private:
+ unsigned long m_StartTime;
+ unsigned long m_WaitMS;
+};
+
+
+// Helper function to get time in milliseconds.
+unsigned long SampleMilliseconds();
+
+
+class ISocket
+{
+public:
+
+ // Call this when you're done.
+ virtual void Release() = 0;
+
+
+ // Bind the socket so you can send and receive with it.
+ // If you bind to port 0, then the system will select the port for you.
+ virtual bool Bind( const CIPAddr *pAddr ) = 0;
+ virtual bool BindToAny( const unsigned short port ) = 0;
+
+
+ // Broadcast some data.
+ virtual bool Broadcast( const void *pData, const int len, const unsigned short port ) = 0;
+
+ // Send a packet.
+ virtual bool SendTo( const CIPAddr *pAddr, const void *pData, const int len ) = 0;
+ virtual bool SendChunksTo( const CIPAddr *pAddr, void const * const *pChunks, const int *pChunkLengths, int nChunks ) = 0;
+
+ // Receive a packet. Returns the length received or -1 if no data came in.
+ // If pFrom is set, then it is filled in with the sender's IP address.
+ virtual int RecvFrom( void *pData, int maxDataLen, CIPAddr *pFrom ) = 0;
+
+ // How long has it been since we successfully received a packet?
+ virtual double GetRecvTimeout() = 0;
+};
+
+// Create a connectionless socket that you can send packets out of.
+ISocket* CreateIPSocket();
+
+// This sets up the socket to receive multicast data on the specified group.
+// By default, localInterface is INADDR_ANY, but if you want to specify a specific interface
+// the data should come in through, you can.
+ISocket* CreateMulticastListenSocket(
+ const CIPAddr &addr,
+ const CIPAddr &localInterface = CIPAddr()
+ );
+
+
+// Setup a CIPAddr from the string. The string can be a dotted IP address or
+// a hostname, and it can be followed by a colon and a port number like "1.2.3.4:3443"
+// or "myhostname.valvesoftware.com:2342".
+//
+// Note: if the string does not contain a port, then pOut->port will be left alone.
+bool ConvertStringToIPAddr( const char *pStr, CIPAddr *pOut );
+
+// Do a DNS lookup on the IP.
+// You can optionally get a service name back too.
+bool ConvertIPAddrToString( const CIPAddr *pIn, char *pOut, int outLen );
+
+
+void IP_GetLastErrorString( char *pStr, int maxLen );
+
+void SockAddrToIPAddr( const struct sockaddr_in *pIn, CIPAddr *pOut );
+void IPAddrToSockAddr( const CIPAddr *pIn, struct sockaddr_in *pOut );
+
+
+#endif
+
diff --git a/mp/src/utils/vmpi/messbuf.h b/mp/src/utils/vmpi/messbuf.h new file mode 100644 index 00000000..2f09e884 --- /dev/null +++ b/mp/src/utils/vmpi/messbuf.h @@ -0,0 +1,52 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// MessageBuffer - handy for packing and upacking
+// structures to be sent as messages
+//
+#ifndef _MESSAGEBUFFER
+#define _MESSAGEBUFFER
+
+#include <stdio.h>
+#define DEFAULT_MESSAGE_BUFFER_SIZE 2048
+
+class MessageBuffer {
+ public:
+ char * data;
+
+ MessageBuffer();
+ MessageBuffer(int size);
+ ~MessageBuffer();
+
+ int getSize();
+ int getLen();
+ int setLen(int len);
+ int getOffset();
+ int setOffset(int offset);
+
+ int write(void const * p, int bytes);
+ int update(int loc, void const * p, int bytes);
+ int extract(int loc, void * p, int bytes);
+ int read(void * p, int bytes);
+
+ int WriteString( const char *pString );
+ int ReadString( char *pOut, int bufferLength );
+
+ void clear();
+ void clear(int minsize);
+ void reset(int minsize);
+ void print(FILE * ofile, int num);
+
+ private:
+ void resize(int minsize);
+ int size;
+ int offset;
+ int len;
+};
+
+#endif
diff --git a/mp/src/utils/vmpi/threadhelpers.h b/mp/src/utils/vmpi/threadhelpers.h new file mode 100644 index 00000000..1e955a5e --- /dev/null +++ b/mp/src/utils/vmpi/threadhelpers.h @@ -0,0 +1,110 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef THREADHELPERS_H
+#define THREADHELPERS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "tier1/utllinkedlist.h"
+
+
+#define SIZEOF_CS 24 // sizeof( CRITICAL_SECTION )
+
+
+class CCriticalSection
+{
+public:
+ CCriticalSection();
+ ~CCriticalSection();
+
+
+protected:
+
+ friend class CCriticalSectionLock;
+
+ void Lock();
+ void Unlock();
+
+
+public:
+ char m_CS[SIZEOF_CS];
+
+ // Used to protect against deadlock in debug mode.
+//#if defined( _DEBUG )
+ CUtlLinkedList<unsigned long,int> m_Locks;
+ char m_DeadlockProtect[SIZEOF_CS];
+//#endif
+};
+
+
+// Use this to lock a critical section.
+class CCriticalSectionLock
+{
+public:
+ CCriticalSectionLock( CCriticalSection *pCS );
+ ~CCriticalSectionLock();
+ void Lock();
+ void Unlock();
+
+private:
+ CCriticalSection *m_pCS;
+ bool m_bLocked;
+};
+
+
+template< class T >
+class CCriticalSectionData : private CCriticalSection
+{
+public:
+ // You only have access to the data between Lock() and Unlock().
+ T* Lock()
+ {
+ CCriticalSection::Lock();
+ return &m_Data;
+ }
+
+ void Unlock()
+ {
+ CCriticalSection::Unlock();
+ }
+
+private:
+ T m_Data;
+};
+
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CEvent.
+// ------------------------------------------------------------------------------------------------ //
+class CEvent
+{
+public:
+ CEvent();
+ ~CEvent();
+
+ bool Init( bool bManualReset, bool bInitialState );
+ void Term();
+
+ void* GetEventHandle() const;
+
+ // Signal the event.
+ bool SetEvent();
+
+ // Unset the event's signalled status.
+ bool ResetEvent();
+
+
+private:
+ void *m_hEvent;
+};
+
+
+#endif // THREADHELPERS_H
diff --git a/mp/src/utils/vmpi/vmpi.h b/mp/src/utils/vmpi/vmpi.h new file mode 100644 index 00000000..b2fe9641 --- /dev/null +++ b/mp/src/utils/vmpi/vmpi.h @@ -0,0 +1,217 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VMPI_H
+#define VMPI_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "vmpi_defs.h"
+#include "messbuf.h"
+#include "iphelpers.h"
+
+
+// These are called to handle incoming messages.
+// Return true if you handled the message and false otherwise.
+// Note: the first byte in each message is the packet ID.
+typedef bool (*VMPIDispatchFn)( MessageBuffer *pBuf, int iSource, int iPacketID );
+
+typedef void (*VMPI_Disconnect_Handler)( int procID, const char *pReason );
+
+
+// Which machine is the master.
+#define VMPI_MASTER_ID 0
+
+#define VMPI_SEND_TO_ALL -2
+#define VMPI_PERSISTENT -3 // If this is set as the destination for a packet, it is sent to all
+ // workers, and also to new workers that connect.
+
+#define MAX_VMPI_PACKET_IDS 32
+
+
+#define VMPI_TIMEOUT_INFINITE 0xFFFFFFFF
+
+
+// Instantiate one of these to register a dispatch.
+class CDispatchReg
+{
+public:
+ CDispatchReg( int iPacketID, VMPIDispatchFn fn );
+};
+
+
+// Enums for all the command line parameters.
+#define VMPI_PARAM_SDK_HIDDEN 0x0001 // Hidden in SDK mode.
+
+#define VMPI_PARAM( paramName, paramFlags, helpText ) paramName,
+enum EVMPICmdLineParam
+{
+ k_eVMPICmdLineParam_FirstParam=0,
+ k_eVMPICmdLineParam_VMPIParam,
+ #include "vmpi_parameters.h"
+ k_eVMPICmdLineParam_LastParam
+};
+#undef VMPI_PARAM
+
+
+// Shared by all the tools.
+extern bool g_bUseMPI;
+extern bool g_bMPIMaster; // Set to true if we're the master in a VMPI session.
+extern int g_iVMPIVerboseLevel; // Higher numbers make it spit out more data.
+
+extern bool g_bMPI_Stats; // Send stats to the MySQL database?
+extern bool g_bMPI_StatsTextOutput; // Send text output in the stats?
+
+// These can be watched or modified to check bandwidth statistics.
+extern int g_nBytesSent;
+extern int g_nMessagesSent;
+extern int g_nBytesReceived;
+extern int g_nMessagesReceived;
+
+extern int g_nMulticastBytesSent;
+extern int g_nMulticastBytesReceived;
+
+extern int g_nMaxWorkerCount;
+
+
+enum VMPIRunMode
+{
+ VMPI_RUN_NETWORKED,
+ VMPI_RUN_LOCAL // Just make a local process and have it do the work.
+};
+
+
+enum VMPIFileSystemMode
+{
+ VMPI_FILESYSTEM_MULTICAST, // Multicast out, find workers, have them do work.
+ VMPI_FILESYSTEM_BROADCAST, // Broadcast out, find workers, have them do work.
+ VMPI_FILESYSTEM_TCP // TCP filesystem.
+};
+
+
+// If this precedes the dependency filename, then it will transfer all the files in the specified directory.
+#define VMPI_DEPENDENCY_DIRECTORY_TOKEN '*'
+
+
+// It's good to specify a disconnect handler here immediately. If you don't have a handler
+// and the master disconnects, you'll lockup forever inside a dispatch loop because you
+// never handled the master disconnecting.
+//
+// Note: runMode is only relevant for the VMPI master. The worker always connects to the master
+// the same way.
+bool VMPI_Init(
+ int &argc,
+ char **&argv,
+ const char *pDependencyFilename,
+ VMPI_Disconnect_Handler handler = NULL,
+ VMPIRunMode runMode = VMPI_RUN_NETWORKED, // Networked or local?,
+ bool bConnectingAsService = false
+ );
+
+// Used when hosting a patch.
+void VMPI_Init_PatchMaster( int argc, char **argv );
+
+void VMPI_Finalize();
+
+VMPIRunMode VMPI_GetRunMode();
+VMPIFileSystemMode VMPI_GetFileSystemMode();
+
+// Note: this number can change on the master.
+int VMPI_GetCurrentNumberOfConnections();
+
+
+// Dispatch messages until it gets one with the specified packet ID.
+// If subPacketID is not set to -1, then the second byte must match that as well.
+//
+// Note: this WILL dispatch packets with matching packet IDs and give them a chance to handle packets first.
+//
+// If bWait is true, then this function either succeeds or Error() is called. If it's false, then if the first available message
+// is handled by a dispatch, this function returns false.
+bool VMPI_DispatchUntil( MessageBuffer *pBuf, int *pSource, int packetID, int subPacketID = -1, bool bWait = true );
+
+// This waits for the next message and dispatches it.
+// You can specify a timeout in milliseconds. If the timeout expires, the function returns false.
+bool VMPI_DispatchNextMessage( unsigned long timeout=VMPI_TIMEOUT_INFINITE );
+
+// This should be called periodically in modal loops that don't call other VMPI functions. This will
+// check for disconnected sockets and call disconnect handlers so the app can error out if
+// it loses all of its connections.
+//
+// This can be used in place of a Sleep() call by specifying a timeout value.
+void VMPI_HandleSocketErrors( unsigned long timeout=0 );
+
+
+
+enum VMPISendFlags
+{
+ k_eVMPISendFlags_GroupPackets = 0x0001
+};
+
+// Use these to send data to one of the machines.
+// If iDest is VMPI_SEND_TO_ALL, then the message goes to all the machines.
+// Flags is a combination of the VMPISendFlags enums.
+bool VMPI_SendData( void *pData, int nBytes, int iDest, int fVMPISendFlags=0 );
+bool VMPI_SendChunks( void const * const *pChunks, const int *pChunkLengths, int nChunks, int iDest, int fVMPISendFlags=0 );
+bool VMPI_Send2Chunks( const void *pChunk1, int chunk1Len, const void *pChunk2, int chunk2Len, int iDest, int fVMPISendFlags=0 ); // for convenience..
+bool VMPI_Send3Chunks( const void *pChunk1, int chunk1Len, const void *pChunk2, int chunk2Len, const void *pChunk3, int chunk3Len, int iDest, int fVMPISendFlags=0 );
+
+// Flush any groups that were queued with k_eVMPISendFlags_GroupPackets.
+// If msInterval is > 0, then it will check a timer and only flush that often (so you can call this a lot, and have it check).
+void VMPI_FlushGroupedPackets( unsigned long msInterval=0 );
+
+// This registers a function that gets called when a connection is terminated ungracefully.
+void VMPI_AddDisconnectHandler( VMPI_Disconnect_Handler handler );
+
+// Returns false if the process has disconnected ungracefully (disconnect handlers
+// would have been called for it too).
+bool VMPI_IsProcConnected( int procID );
+
+// Returns true if the process is just a service (in which case it should only get file IO traffic).
+bool VMPI_IsProcAService( int procID );
+
+// Simple wrapper for Sleep() so people can avoid including windows.h
+void VMPI_Sleep( unsigned long ms );
+
+// VMPI sends machine names around first thing.
+const char* VMPI_GetLocalMachineName();
+const char* VMPI_GetMachineName( int iProc );
+bool VMPI_HasMachineNameBeenSet( int iProc );
+
+// Returns 0xFFFFFFFF if the ID hasn't been set.
+unsigned long VMPI_GetJobWorkerID( int iProc );
+void VMPI_SetJobWorkerID( int iProc, unsigned long jobWorkerID );
+
+// Search a command line to find arguments. Looks for pName, and if it finds it, returns the
+// argument following it. If pName is the last argument, it returns pDefault. If it doesn't
+// find pName, returns NULL.
+const char* VMPI_FindArg( int argc, char **argv, const char *pName, const char *pDefault = "" );
+
+// (Threadsafe) get and set the current stage. This info winds up in the VMPI database.
+void VMPI_GetCurrentStage( char *pOut, int strLen );
+void VMPI_SetCurrentStage( const char *pCurStage );
+
+// VMPI is always broadcasting this job in the background.
+// This changes the password to 'debugworker' and allows more workers in.
+// This can be used if workers are dying on certain work units. Then a programmer
+// can run vmpi_service with -superdebug and debug the whole thing.
+void VMPI_InviteDebugWorkers();
+
+bool VMPI_IsSDKMode();
+
+// Lookup a command line parameter string.
+const char* VMPI_GetParamString( EVMPICmdLineParam eParam );
+int VMPI_GetParamFlags( EVMPICmdLineParam eParam );
+const char* VMPI_GetParamHelpString( EVMPICmdLineParam eParam );
+bool VMPI_IsParamUsed( EVMPICmdLineParam eParam ); // Returns true if the specified parameter is on the command line.
+
+// Can be called from error handlers and if -mpi_Restart is used, it'll automatically restart the process.
+bool VMPI_HandleAutoRestart();
+
+
+#endif // VMPI_H
diff --git a/mp/src/utils/vmpi/vmpi_defs.h b/mp/src/utils/vmpi/vmpi_defs.h new file mode 100644 index 00000000..7845d9f9 --- /dev/null +++ b/mp/src/utils/vmpi/vmpi_defs.h @@ -0,0 +1,147 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VMPI_DEFS_H
+#define VMPI_DEFS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// This goes in front of all packets.
+#define VMPI_PROTOCOL_VERSION 5
+
+// This represents the protocol between the service and its UI.
+#define VMPI_SERVICE_UI_PROTOCOL_VERSION 1
+
+// NOTE: the service version (embedded in vmpi_service.exe as a resource) is the version
+// that is used to apply patches.
+#define VMPI_SERVICE_IDS_VERSION_STRING 102 // This matches IDS_VERSION_STRING in vmpi_service.exe.
+
+// Known packet IDs in various systems.
+#define VMPI_PACKETID_FILESYSTEM 0 // The file system reserves this packet ID.
+ // All application traffic must set its first byte to something other
+ // than this value.
+#define VMPI_SHARED_PACKET_ID 10
+
+
+// Turn this on, and the various service apps will log stuff.
+//#define VMPI_SERVICE_LOGS
+
+
+// This value is put in the RunningTimeMS until the job is finished. This is how
+// the job_search app knows if a job never finished.
+#define RUNNINGTIME_MS_SENTINEL 0xFEDCBAFD
+
+
+
+#define VMPI_SERVICE_NAME_INTERNAL "VMPI"
+#define VMPI_SERVICE_NAME "Valve MPI Service"
+
+// Stuff in the registry goes under here (in HKEY_LOCAL_MACHINE).
+#define VMPI_SERVICE_KEY "Software\\Valve\\VMPI"
+#define SERVICE_INSTALL_LOCATION_KEY "InstallLocation"
+
+// The VMPI service listens on one of these ports to talk to the UI.
+#define VMPI_SERVICE_FIRST_UI_PORT 23300
+#define VMPI_SERVICE_LAST_UI_PORT 23310
+
+// Port numbers that the master will use to broadcast unless -mpi_port is used.
+#define VMPI_MASTER_FIRST_PORT 23311
+#define VMPI_MASTER_LAST_PORT 23330
+
+
+// Packet IDs for vmpi_service to talk to UI clients.
+#define VMPI_SERVICE_TO_UI_CONSOLE_TEXT 0 // Print some text to the UI's console.
+#define VMPI_SERVICE_TO_UI_STATE 1 // Updates state reflecting whether it's idle, busy, etc.
+#define VMPI_SERVICE_TO_UI_PATCHING 2 // Updates state reflecting whether it's idle, busy, etc.
+#define VMPI_SERVICE_TO_UI_EXIT 3 // Updates state reflecting whether it's idle, busy, etc.
+
+ // Application state.. these are communicated between the service and the UI.
+ enum
+ {
+ VMPI_SERVICE_STATE_IDLE=0,
+ VMPI_SERVICE_STATE_BUSY,
+ VMPI_SERVICE_STATE_DISABLED
+ };
+#define VMPI_SERVICE_DISABLE 2 // Stop waiting for jobs..
+#define VMPI_SERVICE_ENABLE 3
+#define VMPI_SERVICE_UPDATE_PASSWORD 4 // New password.
+#define VMPI_SERVICE_EXIT 5 // User chose "exit" from the menu. Kill the service.
+#define VMPI_SERVICE_SKIP_CSX_JOBS 6
+#define VMPI_SERVICE_SCREENSAVER_MODE 7
+
+
+// The worker service waits on this range of ports.
+#define VMPI_SERVICE_PORT 23397
+#define VMPI_LAST_SERVICE_PORT (VMPI_SERVICE_PORT + 15)
+
+
+#define VMPI_WORKER_PORT_FIRST 22340
+#define VMPI_WORKER_PORT_LAST 22350
+
+// The VMPI service downloader is still a worker but it uses this port range so the
+// master knows it's just downloading the exes.
+#define VMPI_SERVICE_DOWNLOADER_PORT_FIRST 22351
+#define VMPI_SERVICE_DOWNLOADER_PORT_LAST 22360
+
+// Give it a small range so they can have multiple masters running.
+#define VMPI_MASTER_PORT_FIRST 21140
+#define VMPI_MASTER_PORT_LAST 21145
+#define VMPI_MASTER_FILESYSTEM_BROADCAST_PORT 21146
+
+
+
+
+// Protocol.
+
+// The message format is:
+// - VMPI_PROTOCOL_VERSION
+// - null-terminated password string (or VMPI_PASSWORD_OVERRIDE followed by a zero to process it regardless of pw).
+// - packet ID
+// - payload
+
+
+#define VMPI_PASSWORD_OVERRIDE -111
+
+
+#define VMPI_MESSAGE_BASE 71
+
+
+// This is the broadcast message from the main (rank 0) process looking for workers.
+#define VMPI_LOOKING_FOR_WORKERS (VMPI_MESSAGE_BASE+0)
+
+// This is so an app can find out what machines are running the service.
+#define VMPI_PING_REQUEST (VMPI_MESSAGE_BASE+2)
+#define VMPI_PING_RESPONSE (VMPI_MESSAGE_BASE+3)
+
+// This tells the service to quit.
+#define VMPI_STOP_SERVICE (VMPI_MESSAGE_BASE+6)
+
+// This tells the service to kill any process it has running.
+#define VMPI_KILL_PROCESS (VMPI_MESSAGE_BASE+7)
+
+// This tells the service to patch itself.
+#define VMPI_SERVICE_PATCH (VMPI_MESSAGE_BASE+8)
+
+// Sent back to the master via UDP to tell it if its job has started and ended.
+#define VMPI_NOTIFY_START_STATUS (VMPI_MESSAGE_BASE+9)
+#define VMPI_NOTIFY_END_STATUS (VMPI_MESSAGE_BASE+10)
+
+#define VMPI_FORCE_PASSWORD_CHANGE (VMPI_MESSAGE_BASE+11)
+
+
+// These states are sent from the service to the services browser.
+#define VMPI_STATE_IDLE 0
+#define VMPI_STATE_BUSY 1
+#define VMPI_STATE_PATCHING 2
+#define VMPI_STATE_DISABLED 3
+#define VMPI_STATE_SCREENSAVER_DISABLED 4
+#define VMPI_STATE_DOWNLOADING 5
+
+
+#endif // VMPI_DEFS_H
diff --git a/mp/src/utils/vmpi/vmpi_dispatch.h b/mp/src/utils/vmpi/vmpi_dispatch.h new file mode 100644 index 00000000..d91c5b1d --- /dev/null +++ b/mp/src/utils/vmpi/vmpi_dispatch.h @@ -0,0 +1,15 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VMPI_DISPATCH_H
+#define VMPI_DISPATCH_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#endif // VMPI_DISPATCH_H
diff --git a/mp/src/utils/vmpi/vmpi_distribute_work.h b/mp/src/utils/vmpi/vmpi_distribute_work.h new file mode 100644 index 00000000..cfd37d8b --- /dev/null +++ b/mp/src/utils/vmpi/vmpi_distribute_work.h @@ -0,0 +1,89 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef VMPI_DISTRIBUTE_WORK_H
+#define VMPI_DISTRIBUTE_WORK_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "messbuf.h"
+#include "utlvector.h"
+
+
+class IWorkUnitDistributorCallbacks
+{
+public:
+ // Called every 200ms or so as it does the work.
+ // Return true to stop distributing work.
+ virtual bool Update() { return false; }
+
+ // Called when a subsequent number of work units is completed.
+ // e.g. results received in the following order will trigger
+ // the following calls to OnWorkUnitsCompleted:
+ // Work unit numbers: wu2 wu4 wu5 wu1 wu0 wu6 wu3
+ // Calling OnWorkUnitsCompleted with arg: - - - - 3 - 7
+ // because when wu0 is received we already have { wu0, wu1, wu2 } so we signal
+ // that 3 subsequent work units completed, like wise by the time when wu3 is
+ // received we already have a full set { wu0, wu1, wu2, wu3, wu4, wu5, wu6 }
+ // and signal that 7 work units completed.
+ virtual void OnWorkUnitsCompleted( uint64 numWorkUnits ) { return; }
+};
+
+
+enum EWorkUnitDistributor
+{
+ k_eWorkUnitDistributor_Default,
+ k_eWorkUnitDistributor_SDK
+};
+
+// Tells which work unit distributor is going to be used.
+EWorkUnitDistributor VMPI_GetActiveWorkUnitDistributor();
+
+
+// Before calling DistributeWork, you can set this and it'll call your virtual functions.
+extern IWorkUnitDistributorCallbacks *g_pDistributeWorkCallbacks;
+
+
+// You must append data to pBuf with the work unit results.
+// Note: pBuf will be NULL if this is a local thread doing work on the master.
+typedef void (*ProcessWorkUnitFn)( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf );
+
+// pBuf is ready to read the results written to the buffer in ProcessWorkUnitFn.
+typedef void (*ReceiveWorkUnitFn)( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker );
+
+
+// Use a CDispatchReg to register this function with whatever packet ID you give to DistributeWork.
+bool DistributeWorkDispatch( MessageBuffer *pBuf, int iSource, int iPacketID );
+
+
+
+// This is the function vrad and vvis use to divide the work units and send them out.
+// It maintains a sliding window of work units so it can always keep the clients busy.
+//
+// The workers implement processFn to do the job work in a work unit.
+// This function must send back a packet formatted with:
+// cPacketID (char), cSubPacketID (char), iWorkUnit (int), (app-specific data for the results)
+//
+// The masters implement receiveFn to receive a work unit's results.
+//
+// Returns time it took to finish the work.
+double DistributeWork(
+ uint64 nWorkUnits, // how many work units to dole out
+ char cPacketID, // This packet ID must be reserved for DistributeWork and DistributeWorkDispatch
+ // must be registered with it.
+ ProcessWorkUnitFn processFn, // workers implement this to process a work unit and send results back
+ ReceiveWorkUnitFn receiveFn // the master implements this to receive a work unit
+ );
+
+
+// VMPI calls this before shutting down because any threads that DistributeWork has running must stop,
+// otherwise it can crash if a thread tries to send data in the middle of shutting down.
+void DistributeWork_Cancel();
+
+
+#endif // VMPI_DISTRIBUTE_WORK_H
diff --git a/mp/src/utils/vmpi/vmpi_filesystem.h b/mp/src/utils/vmpi/vmpi_filesystem.h new file mode 100644 index 00000000..889d8abc --- /dev/null +++ b/mp/src/utils/vmpi/vmpi_filesystem.h @@ -0,0 +1,53 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VMPI_FILESYSTEM_H
+#define VMPI_FILESYSTEM_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "interface.h"
+
+
+class IFileSystem;
+class MessageBuffer;
+
+
+// Use this to read virtual files.
+#define VMPI_VIRTUAL_FILES_PATH_ID "VMPI_VIRTUAL_FILES_PATH_ID"
+
+
+// When you hook the file system with VMPI and are a worker, it blocks on file reads
+// and uses MPI to communicate with the master to transfer files it needs over.
+//
+// The filesystem, by default (and it maxFileSystemMemoryUsage is left at zero),
+// keeps the contents of the files that get opened in memory. You can pass in a
+// value here to put a cap on it, in which case it'll unload the least-recently-used
+// files when it hits the limit.
+IFileSystem* VMPI_FileSystem_Init( int maxFileSystemMemoryUsage, IFileSystem *pPassThru );
+
+// On the master machine, this really should be called before the app shuts down and
+// global destructors are called. If it isn't, it might lock up waiting for a thread to exit.
+//
+// This returns the original filesystem you passed into VMPI_FileSystem_Init so you can uninitialize it.
+IFileSystem* VMPI_FileSystem_Term();
+
+// Causes it to error out on any Open() calls.
+void VMPI_FileSystem_DisableFileAccess();
+
+// Returns a factory that will hand out BASEFILESYSTEM_INTERFACE_VERSION when asked for it.
+CreateInterfaceFn VMPI_FileSystem_GetFactory();
+
+// This function creates a virtual file that workers can then open and read out of.
+// NOTE: when reading from the file, you must use VMPI_VIRTUAL_FILES_PATH_ID as the path ID
+// or else it won't find the file.
+void VMPI_FileSystem_CreateVirtualFile( const char *pFilename, const void *pData, unsigned long fileLength );
+
+
+#endif // VMPI_FILESYSTEM_H
diff --git a/mp/src/utils/vmpi/vmpi_parameters.h b/mp/src/utils/vmpi/vmpi_parameters.h new file mode 100644 index 00000000..db467cd0 --- /dev/null +++ b/mp/src/utils/vmpi/vmpi_parameters.h @@ -0,0 +1,31 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+VMPI_PARAM( mpi_Worker, 0, "Workers use this to connect to a VMPI job. Specify the IP address of the master. Example: -mpi_worker 1.2.3.4 or -mpi_worker 1.2.3.4:242" )
+VMPI_PARAM( mpi_Port, 0, "Use this on the master to force it to bind to a specified port. Otherwise it binds to 23311 (and ascending port numbers if 23311 doesn't work)." )
+VMPI_PARAM( mpi_Graphics, 0, "Show a graphical representation of work units [grey=work unit not sent yet, red=sent, green=completed, blue=in-process]" )
+VMPI_PARAM( mpi_Retry, 0, "Use this on the worker to have it retry connecting to the master forever. Otherwise it will exit if it can't connect to the master immediately." )
+VMPI_PARAM( mpi_AutoRestart, 0, "Use this on the worker to have it restart with the same command line parameters after completing a job. Useful in conjunction with -mpi_Retry to have an always-on worker ready to do work." )
+VMPI_PARAM( mpi_TrackEvents, 0, "Enables a debug menu during jobs (press D to access). Note: -mpi_Graphics automatically enables -mpi_TrackEvents." )
+VMPI_PARAM( mpi_ShowDistributeWorkStats, 0, "After finishing a stage in the work unit processing, shows statistics." )
+VMPI_PARAM( mpi_TimingWait, 0, "Causes the master to wait for a keypress to start so workers can connect before it starts. Used for performance measurements." )
+VMPI_PARAM( mpi_WorkerCount, 0, "Set the maximum number of workers allowed in the job." )
+VMPI_PARAM( mpi_AutoLocalWorker, 0, "Used on the master's machine. Automatically spawn a worker on the local machine. Used for testing." )
+VMPI_PARAM( mpi_FileTransmitRate, 0, "VMPI file transmission rate in kB/sec." )
+VMPI_PARAM( mpi_Verbose, 0, "Set to 0, 1, or 2 to control verbosity of debug output." )
+VMPI_PARAM( mpi_NoMasterWorkerThreads, 0, "Don't process work units locally (in the master). Only used by the SDK work unit distributor." )
+VMPI_PARAM( mpi_SDKMode, VMPI_PARAM_SDK_HIDDEN, "Force VMPI to run in SDK mode." )
+VMPI_PARAM( mpi_UseSDKDistributor, VMPI_PARAM_SDK_HIDDEN, "Use the SDK work unit distributor. Optimized for low numbers of workers and higher latency. Note that this will automatically be used in SDK distributions." )
+VMPI_PARAM( mpi_UseDefaultDistributor, VMPI_PARAM_SDK_HIDDEN, "Use the default work unit distributor. Optimized for high numbers of workers, higher numbers of work units, and lower latency. Note that this will automatically be used in non-SDK distributions." )
+VMPI_PARAM( mpi_NoTimeout, VMPI_PARAM_SDK_HIDDEN, "Don't timeout VMPI sockets. Used for testing." )
+VMPI_PARAM( mpi_DontSetThreadPriorities, VMPI_PARAM_SDK_HIDDEN, "Don't set worker thread priorities to idle." )
+VMPI_PARAM( mpi_GroupPackets, VMPI_PARAM_SDK_HIDDEN, "Delay and group some of the worker packets instead of sending immediately." )
+VMPI_PARAM( mpi_Stats, VMPI_PARAM_SDK_HIDDEN, "Enables the use of a database to store compile statistics." )
+VMPI_PARAM( mpi_Stats_TextOutput, VMPI_PARAM_SDK_HIDDEN, "Enables the workers storing all of their text output into the stats database." )
+VMPI_PARAM( mpi_pw, VMPI_PARAM_SDK_HIDDEN, "Non-SDK only. Sets a password on the VMPI job. Workers must also use the same -mpi_pw [password] argument or else the master will ignore their requests to join the job." )
+VMPI_PARAM( mpi_CalcShuffleCRC, VMPI_PARAM_SDK_HIDDEN, "Calculate a CRC for shuffled work unit arrays in the SDK work unit distributor." )
+VMPI_PARAM( mpi_Job_Watch, VMPI_PARAM_SDK_HIDDEN, "Automatically launches vmpi_job_watch.exe on the job." )
+VMPI_PARAM( mpi_Local, VMPI_PARAM_SDK_HIDDEN, "Similar to -mpi_AutoLocalWorker, but the automatically-spawned worker's console window is hidden." )
\ No newline at end of file diff --git a/mp/src/utils/vrad/disp_vrad.cpp b/mp/src/utils/vrad/disp_vrad.cpp new file mode 100644 index 00000000..e1ef3a2e --- /dev/null +++ b/mp/src/utils/vrad/disp_vrad.cpp @@ -0,0 +1,332 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "disp_vrad.h"
+#include "utllinkedlist.h"
+#include "utlvector.h"
+#include "iscratchpad3d.h"
+#include "scratchpadutils.h"
+
+
+//#define USE_SCRATCHPAD
+#if defined( USE_SCRATCHPAD )
+ static IScratchPad3D *g_pPad = 0;
+#endif
+
+
+int FindNeighborCornerVert( CCoreDispInfo *pDisp, const Vector &vTest )
+{
+ CDispUtilsHelper *pDispHelper = pDisp;
+
+ int iClosest = 0;
+ float flClosest = 1e24;
+ for ( int iCorner=0; iCorner < 4; iCorner++ )
+ {
+ // Has it been touched?
+ CVertIndex cornerVert = pDispHelper->GetPowerInfo()->GetCornerPointIndex( iCorner );
+ int iCornerVert = pDispHelper->VertIndexToInt( cornerVert );
+ const Vector &vCornerVert = pDisp->GetVert( iCornerVert );
+
+ float flDist = vCornerVert.DistTo( vTest );
+ if ( flDist < flClosest )
+ {
+ iClosest = iCorner;
+ flClosest = flDist;
+ }
+ }
+
+ if ( flClosest <= 0.1f )
+ return iClosest;
+ else
+ return -1;
+}
+
+
+int GetAllNeighbors( const CCoreDispInfo *pDisp, int iNeighbors[512] )
+{
+ int nNeighbors = 0;
+
+ // Check corner neighbors.
+ for ( int iCorner=0; iCorner < 4; iCorner++ )
+ {
+ const CDispCornerNeighbors *pCorner = pDisp->GetCornerNeighbors( iCorner );
+
+ for ( int i=0; i < pCorner->m_nNeighbors; i++ )
+ {
+ if ( nNeighbors < _ARRAYSIZE( iNeighbors ) )
+ iNeighbors[nNeighbors++] = pCorner->m_Neighbors[i];
+ }
+ }
+
+ for ( int iEdge=0; iEdge < 4; iEdge++ )
+ {
+ const CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
+
+ for ( int i=0; i < 2; i++ )
+ {
+ if ( pEdge->m_SubNeighbors[i].IsValid() )
+ if ( nNeighbors < 512 )
+ iNeighbors[nNeighbors++] = pEdge->m_SubNeighbors[i].GetNeighborIndex();
+ }
+ }
+
+ return nNeighbors;
+}
+
+
+void BlendCorners( CCoreDispInfo **ppListBase, int listSize )
+{
+ CUtlVector<int> nbCornerVerts;
+
+ for ( int iDisp=0; iDisp < listSize; iDisp++ )
+ {
+ CCoreDispInfo *pDisp = ppListBase[iDisp];
+
+ int iNeighbors[512];
+ int nNeighbors = GetAllNeighbors( pDisp, iNeighbors );
+
+ // Make sure we have room for all the neighbors.
+ nbCornerVerts.RemoveAll();
+ nbCornerVerts.EnsureCapacity( nNeighbors );
+ nbCornerVerts.AddMultipleToTail( nNeighbors );
+
+ // For each corner.
+ for ( int iCorner=0; iCorner < 4; iCorner++ )
+ {
+ // Has it been touched?
+ CVertIndex cornerVert = pDisp->GetCornerPointIndex( iCorner );
+ int iCornerVert = pDisp->VertIndexToInt( cornerVert );
+ const Vector &vCornerVert = pDisp->GetVert( iCornerVert );
+
+ // For each displacement sharing this corner..
+ Vector vAverage = pDisp->GetNormal( iCornerVert );
+
+ for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
+ {
+ int iNBListIndex = iNeighbors[iNeighbor];
+ CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
+
+ // Find out which vert it is on the neighbor.
+ int iNBCorner = FindNeighborCornerVert( pNeighbor, vCornerVert );
+ if ( iNBCorner == -1 )
+ {
+ nbCornerVerts[iNeighbor] = -1; // remove this neighbor from the list.
+ }
+ else
+ {
+ CVertIndex viNBCornerVert = pNeighbor->GetCornerPointIndex( iNBCorner );
+ int iNBVert = pNeighbor->VertIndexToInt( viNBCornerVert );
+ nbCornerVerts[iNeighbor] = iNBVert;
+ vAverage += pNeighbor->GetNormal( iNBVert );
+ }
+ }
+
+
+ // Blend all the neighbor normals with this one.
+ VectorNormalize( vAverage );
+ pDisp->SetNormal( iCornerVert, vAverage );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple(
+ g_pPad,
+ pDisp->GetVert( iCornerVert ),
+ pDisp->GetNormal( iCornerVert ),
+ Vector( 0, 0, 1 ),
+ 25 );
+#endif
+
+ for ( int iNeighbor=0; iNeighbor < nNeighbors; iNeighbor++ )
+ {
+ int iNBListIndex = iNeighbors[iNeighbor];
+ if ( nbCornerVerts[iNeighbor] == -1 )
+ continue;
+
+ CCoreDispInfo *pNeighbor = ppListBase[iNBListIndex];
+ pNeighbor->SetNormal( nbCornerVerts[iNeighbor], vAverage );
+ }
+ }
+ }
+}
+
+
+void BlendTJuncs( CCoreDispInfo **ppListBase, int listSize )
+{
+ for ( int iDisp=0; iDisp < listSize; iDisp++ )
+ {
+ CCoreDispInfo *pDisp = ppListBase[iDisp];
+
+ for ( int iEdge=0; iEdge < 4; iEdge++ )
+ {
+ CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
+
+ CVertIndex viMidPoint = pDisp->GetEdgeMidPoint( iEdge );
+ int iMidPoint = pDisp->VertIndexToInt( viMidPoint );
+
+ if ( pEdge->m_SubNeighbors[0].IsValid() && pEdge->m_SubNeighbors[1].IsValid() )
+ {
+ const Vector &vMidPoint = pDisp->GetVert( iMidPoint );
+
+ CCoreDispInfo *pNeighbor1 = ppListBase[pEdge->m_SubNeighbors[0].GetNeighborIndex()];
+ CCoreDispInfo *pNeighbor2 = ppListBase[pEdge->m_SubNeighbors[1].GetNeighborIndex()];
+
+ int iNBCorners[2];
+ iNBCorners[0] = FindNeighborCornerVert( pNeighbor1, vMidPoint );
+ iNBCorners[1] = FindNeighborCornerVert( pNeighbor2, vMidPoint );
+
+ if ( iNBCorners[0] != -1 && iNBCorners[1] != -1 )
+ {
+ CVertIndex viNBCorners[2] =
+ {
+ pNeighbor1->GetCornerPointIndex( iNBCorners[0] ),
+ pNeighbor2->GetCornerPointIndex( iNBCorners[1] )
+ };
+
+ Vector vAverage = pDisp->GetNormal( iMidPoint );
+ vAverage += pNeighbor1->GetNormal( viNBCorners[0] );
+ vAverage += pNeighbor2->GetNormal( viNBCorners[1] );
+
+ VectorNormalize( vAverage );
+ pDisp->SetNormal( iMidPoint, vAverage );
+ pNeighbor1->SetNormal( viNBCorners[0], vAverage );
+ pNeighbor2->SetNormal( viNBCorners[1], vAverage );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( iMidPoint ), pDisp->GetNormal( iMidPoint ), Vector( 0, 1, 1 ), 25 );
+#endif
+ }
+ }
+ }
+ }
+}
+
+
+void BlendEdges( CCoreDispInfo **ppListBase, int listSize )
+{
+ for ( int iDisp=0; iDisp < listSize; iDisp++ )
+ {
+ CCoreDispInfo *pDisp = ppListBase[iDisp];
+
+ for ( int iEdge=0; iEdge < 4; iEdge++ )
+ {
+ CDispNeighbor *pEdge = pDisp->GetEdgeNeighbor( iEdge );
+
+ for ( int iSub=0; iSub < 2; iSub++ )
+ {
+ CDispSubNeighbor *pSub = &pEdge->m_SubNeighbors[iSub];
+ if ( !pSub->IsValid() )
+ continue;
+
+ CCoreDispInfo *pNeighbor = ppListBase[ pSub->GetNeighborIndex() ];
+
+ int iEdgeDim = g_EdgeDims[iEdge];
+
+ CDispSubEdgeIterator it;
+ it.Start( pDisp, iEdge, iSub, true );
+
+ // Get setup on the first corner vert.
+ it.Next();
+ CVertIndex viPrevPos = it.GetVertIndex();
+
+ while ( it.Next() )
+ {
+ // Blend the two.
+ if ( !it.IsLastVert() )
+ {
+ Vector vAverage = pDisp->GetNormal( it.GetVertIndex() ) + pNeighbor->GetNormal( it.GetNBVertIndex() );
+ VectorNormalize( vAverage );
+
+ pDisp->SetNormal( it.GetVertIndex(), vAverage );
+ pNeighbor->SetNormal( it.GetNBVertIndex(), vAverage );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( it.GetVertIndex() ), pDisp->GetNormal( it.GetVertIndex() ), Vector( 1, 0, 0 ), 25 );
+#endif
+ }
+
+ // Now blend the in-between verts (if this edge is high-res).
+ int iPrevPos = viPrevPos[ !iEdgeDim ];
+ int iCurPos = it.GetVertIndex()[ !iEdgeDim ];
+
+ for ( int iTween = iPrevPos+1; iTween < iCurPos; iTween++ )
+ {
+ float flPercent = RemapVal( iTween, iPrevPos, iCurPos, 0, 1 );
+ Vector vNormal;
+ VectorLerp( pDisp->GetNormal( viPrevPos ), pDisp->GetNormal( it.GetVertIndex() ), flPercent, vNormal );
+ VectorNormalize( vNormal );
+
+ CVertIndex viTween;
+ viTween[iEdgeDim] = it.GetVertIndex()[ iEdgeDim ];
+ viTween[!iEdgeDim] = iTween;
+ pDisp->SetNormal( viTween, vNormal );
+
+#if defined( USE_SCRATCHPAD )
+ ScratchPad_DrawArrowSimple( g_pPad, pDisp->GetVert( viTween ), pDisp->GetNormal( viTween ), Vector( 1, 0.5, 0 ), 25 );
+#endif
+ }
+
+ viPrevPos = it.GetVertIndex();
+ }
+ }
+ }
+ }
+}
+
+
+#if defined( USE_SCRATCHPAD )
+ void ScratchPad_DrawOriginalNormals( const CCoreDispInfo *pListBase, int listSize )
+ {
+ for ( int i=0; i < listSize; i++ )
+ {
+ const CCoreDispInfo *pDisp = &pListBase[i];
+ const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo();
+
+ // Draw the triangles.
+ for ( int iTri=0; iTri < pPowerInfo->GetNumTriInfos(); iTri++ )
+ {
+ const CTriInfo *pTriInfo = pPowerInfo->GetTriInfo( iTri );
+
+ for ( int iLine=0; iLine < 3; iLine++ )
+ {
+ const Vector &v1 = pDisp->GetVert( pTriInfo->m_Indices[iLine] );
+ const Vector &v2 = pDisp->GetVert( pTriInfo->m_Indices[(iLine+1)%3] );
+
+ g_pPad->DrawLine( CSPVert( v1 ), CSPVert( v2 ) );
+ }
+ }
+
+ // Draw the normals.
+ CDispCircumferenceIterator it( pPowerInfo->GetSideLength() );
+ while ( it.Next() )
+ {
+ ScratchPad_DrawArrowSimple(
+ g_pPad,
+ pDisp->GetVert( it.GetVertIndex() ),
+ pDisp->GetNormal( it.GetVertIndex() ),
+ Vector( 0, 1, 0 ),
+ 15 );
+ }
+ }
+ }
+#endif
+
+
+void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppListBase, int listSize )
+{
+//#if defined( USE_SCRATCHPAD )
+// g_pPad = ScratchPad3D_Create();
+// ScratchPad_DrawOriginalNormals( pListBase, listSize );
+//#endif
+
+ BlendTJuncs( ppListBase, listSize );
+
+ BlendCorners( ppListBase, listSize );
+
+ BlendEdges( ppListBase, listSize );
+}
+
+
+
diff --git a/mp/src/utils/vrad/disp_vrad.h b/mp/src/utils/vrad/disp_vrad.h new file mode 100644 index 00000000..ec6bb838 --- /dev/null +++ b/mp/src/utils/vrad/disp_vrad.h @@ -0,0 +1,22 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DISP_VRAD_H
+#define DISP_VRAD_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "builddisp.h"
+
+
+// Blend the normals of neighboring displacement surfaces so they match at edges and corners.
+void SmoothNeighboringDispSurfNormals( CCoreDispInfo **ppListBase, int listSize );
+
+
+#endif // DISP_VRAD_H
diff --git a/mp/src/utils/vrad/iincremental.h b/mp/src/utils/vrad/iincremental.h new file mode 100644 index 00000000..fbde8c86 --- /dev/null +++ b/mp/src/utils/vrad/iincremental.h @@ -0,0 +1,71 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef IINCREMENTAL_H
+#define IINCREMENTAL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "mathlib/vector.h"
+#include "utlvector.h"
+
+
+typedef unsigned short IncrementalLightID;
+
+
+// Incremental lighting manager.
+class IIncremental
+{
+// IIncremental overrides.
+public:
+
+ virtual ~IIncremental() {}
+
+ // Sets up for incremental mode. The BSP file (in bsplib) should be loaded
+ // already so it can detect if the incremental file is up to date.
+ virtual bool Init( char const *pBSPFilename, char const *pIncrementalFilename ) = 0;
+
+ // Prepare to light. You must call Init once, but then you can
+ // do as many Prepare/AddLight/Finalize phases as you want.
+ virtual bool PrepareForLighting() = 0;
+
+ // Called every time light is added to a face.
+ // NOTE: This is the ONLY threadsafe function in IIncremental.
+ virtual void AddLightToFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iSample,
+ int lmSize,
+ float dot,
+ int iThread ) = 0;
+
+ // Called when it's done applying light from the specified light to the specified face.
+ virtual void FinishFace (
+ IncrementalLightID lightID,
+ int iFace,
+ int iThread ) = 0;
+
+ // For each face that was changed during the lighting process, save out
+ // new data for it in the incremental file.
+ // Returns false if the incremental lighting isn't active.
+ virtual bool Finalize() = 0;
+
+ // Grows touched to a size of 'numfaces' and sets each byte to 0 or 1 telling
+ // if the face's lightmap was updated in Finalize.
+ virtual void GetFacesTouched( CUtlVector<unsigned char> &touched ) = 0;
+
+ // This saves the .r0 file and updates the lighting in the BSP file.
+ virtual bool Serialize() = 0;
+};
+
+
+extern IIncremental* GetIncremental();
+
+
+#endif // IINCREMENTAL_H
diff --git a/mp/src/utils/vrad/imagepacker.cpp b/mp/src/utils/vrad/imagepacker.cpp new file mode 100644 index 00000000..612b0d57 --- /dev/null +++ b/mp/src/utils/vrad/imagepacker.cpp @@ -0,0 +1,141 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================
+
+#include "vrad.h"
+#include "imagepacker.h"
+
+
+bool CImagePacker::Reset( int maxLightmapWidth, int maxLightmapHeight )
+{
+ int i;
+
+ Assert( maxLightmapWidth <= MAX_MAX_LIGHTMAP_WIDTH );
+
+ m_MaxLightmapWidth = maxLightmapWidth;
+ m_MaxLightmapHeight = maxLightmapHeight;
+
+ m_MaxBlockWidth = maxLightmapWidth + 1;
+ m_MaxBlockHeight = maxLightmapHeight + 1;
+
+ m_AreaUsed = 0;
+ m_MinimumHeight = -1;
+ for( i = 0; i < m_MaxLightmapWidth; i++ )
+ {
+ m_pLightmapWavefront[i] = -1;
+ }
+ return true;
+}
+
+
+inline int CImagePacker::GetMaxYIndex( int firstX, int width )
+{
+ int maxY = -1;
+ int maxYIndex = 0;
+ for( int x = firstX; x < firstX + width; ++x )
+ {
+ // NOTE: Want the equals here since we'll never be able to fit
+ // in between the multiple instances of maxY
+ if( m_pLightmapWavefront[x] >= maxY )
+ {
+ maxY = m_pLightmapWavefront[x];
+ maxYIndex = x;
+ }
+ }
+ return maxYIndex;
+}
+
+
+bool CImagePacker::AddBlock( int width, int height, int *returnX, int *returnY )
+{
+ // If we've already determined that a block this big couldn't fit
+ // then blow off checking again...
+ if ( ( width >= m_MaxBlockWidth ) && ( height >= m_MaxBlockHeight ) )
+ return false;
+
+ int bestX = -1;
+ int maxYIdx;
+ int outerX = 0;
+ int outerMinY = m_MaxLightmapHeight;
+ int lastX = m_MaxLightmapWidth - width;
+ int lastMaxYVal = -2;
+ while (outerX <= lastX)
+ {
+ // Skip all tiles that have the last Y value, these
+ // aren't going to change our min Y value
+ if (m_pLightmapWavefront[outerX] == lastMaxYVal)
+ {
+ ++outerX;
+ continue;
+ }
+
+ maxYIdx = GetMaxYIndex( outerX, width );
+ lastMaxYVal = m_pLightmapWavefront[maxYIdx];
+ if (outerMinY > lastMaxYVal)
+ {
+ outerMinY = lastMaxYVal;
+ bestX = outerX;
+ }
+ outerX = maxYIdx + 1;
+ }
+
+ if( bestX == -1 )
+ {
+ // If we failed to add it, remember the block size that failed
+ // *only if both dimensions are smaller*!!
+ // Just because a 1x10 block failed, doesn't mean a 10x1 block will fail
+ if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
+ {
+ m_MaxBlockWidth = width;
+ m_MaxBlockHeight = height;
+ }
+
+ return false;
+ }
+
+ // Set the return positions for the block.
+ *returnX = bestX;
+ *returnY = outerMinY + 1;
+
+ // Check if it actually fit height-wise.
+ // hack
+ // if( *returnY + height > maxLightmapHeight )
+ if( *returnY + height >= m_MaxLightmapHeight - 1 )
+ {
+ if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
+ {
+ m_MaxBlockWidth = width;
+ m_MaxBlockHeight = height;
+ }
+
+ return false;
+ }
+
+ // It fit!
+ // Keep up with the smallest possible size for the image so far.
+ if( *returnY + height > m_MinimumHeight )
+ m_MinimumHeight = *returnY + height;
+
+ // Update the wavefront info.
+ int x;
+ for( x = bestX; x < bestX + width; x++ )
+ {
+ m_pLightmapWavefront[x] = outerMinY + height;
+ }
+
+ // AddBlockToLightmapImage( *returnX, *returnY, width, height );
+ m_AreaUsed += width * height;
+
+ return true;
+}
+
diff --git a/mp/src/utils/vrad/imagepacker.h b/mp/src/utils/vrad/imagepacker.h new file mode 100644 index 00000000..0ee1f50e --- /dev/null +++ b/mp/src/utils/vrad/imagepacker.h @@ -0,0 +1,51 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================
+
+#ifndef IMAGEPACKER_H
+#define IMAGEPACKER_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define MAX_MAX_LIGHTMAP_WIDTH 2048
+
+
+//-----------------------------------------------------------------------------
+// This packs a single lightmap
+//-----------------------------------------------------------------------------
+class CImagePacker
+{
+public:
+ bool Reset( int maxLightmapWidth, int maxLightmapHeight );
+ bool AddBlock( int width, int height, int *returnX, int *returnY );
+
+protected:
+ int GetMaxYIndex( int firstX, int width );
+
+ int m_MaxLightmapWidth;
+ int m_MaxLightmapHeight;
+ int m_pLightmapWavefront[MAX_MAX_LIGHTMAP_WIDTH];
+ int m_AreaUsed;
+ int m_MinimumHeight;
+
+ // For optimization purposes:
+ // These store the width + height of the first image
+ // that was unable to be stored in this image
+ int m_MaxBlockWidth;
+ int m_MaxBlockHeight;
+};
+
+
+#endif // IMAGEPACKER_H
diff --git a/mp/src/utils/vrad/incremental.cpp b/mp/src/utils/vrad/incremental.cpp new file mode 100644 index 00000000..9dd877e0 --- /dev/null +++ b/mp/src/utils/vrad/incremental.cpp @@ -0,0 +1,766 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "incremental.h"
+#include "lightmap.h"
+
+
+
+static bool g_bFileError = false;
+
+
+// -------------------------------------------------------------------------------- //
+// Static helpers.
+// -------------------------------------------------------------------------------- //
+
+static bool CompareLights( dworldlight_t *a, dworldlight_t *b )
+{
+ static float flEpsilon = 1e-7;
+
+ bool a1 = VectorsAreEqual( a->origin, b->origin, flEpsilon );
+ bool a2 = VectorsAreEqual( a->intensity, b->intensity, 1.1f ); // intensities are huge numbers
+ bool a3 = VectorsAreEqual( a->normal, b->normal, flEpsilon );
+ bool a4 = fabs( a->constant_attn - b->constant_attn ) < flEpsilon;
+ bool a5 = fabs( a->linear_attn - b->linear_attn ) < flEpsilon;
+ bool a6 = fabs( a->quadratic_attn - b->quadratic_attn ) < flEpsilon;
+ bool a7 = fabs( float( a->flags - b->flags ) ) < flEpsilon;
+ bool a8 = fabs( a->stopdot - b->stopdot ) < flEpsilon;
+ bool a9 = fabs( a->stopdot2 - b->stopdot2 ) < flEpsilon;
+ bool a10 = fabs( a->exponent - b->exponent ) < flEpsilon;
+ bool a11 = fabs( a->radius - b->radius ) < flEpsilon;
+
+ return a1 && a2 && a3 && a4 && a5 && a6 && a7 && a8 && a9 && a10 && a11;
+}
+
+
+long FileOpen( char const *pFilename, bool bRead )
+{
+ g_bFileError = false;
+ return (long)g_pFileSystem->Open( pFilename, bRead ? "rb" : "wb" );
+}
+
+
+void FileClose( long fp )
+{
+ if( fp )
+ g_pFileSystem->Close( (FILE*)fp );
+}
+
+
+// Returns true if there was an error reading from the file.
+bool FileError()
+{
+ return g_bFileError;
+}
+
+static inline void FileRead( long fp, void *pOut, int size )
+{
+ if( g_bFileError || g_pFileSystem->Read( pOut, size, (FileHandle_t)fp ) != size )
+ {
+ g_bFileError = true;
+ memset( pOut, 0, size );
+ }
+}
+
+
+template<class T>
+static inline void FileRead( long fp, T &out )
+{
+ FileRead( fp, &out, sizeof(out) );
+}
+
+
+static inline void FileWrite( long fp, void const *pData, int size )
+{
+ if( g_bFileError || g_pFileSystem->Write( pData, size, (FileHandle_t)fp ) != size )
+ {
+ g_bFileError = true;
+ }
+}
+
+
+template<class T>
+static inline void FileWrite( long fp, T out )
+{
+ FileWrite( fp, &out, sizeof(out) );
+}
+
+
+IIncremental* GetIncremental()
+{
+ static CIncremental inc;
+ return &inc;
+}
+
+
+// -------------------------------------------------------------------------------- //
+// CIncremental.
+// -------------------------------------------------------------------------------- //
+
+CIncremental::CIncremental()
+{
+ m_TotalMemory = 0;
+ m_pIncrementalFilename = NULL;
+ m_pBSPFilename = NULL;
+ m_bSuccessfulRun = false;
+}
+
+
+CIncremental::~CIncremental()
+{
+}
+
+
+bool CIncremental::Init( char const *pBSPFilename, char const *pIncrementalFilename )
+{
+ m_pBSPFilename = pBSPFilename;
+ m_pIncrementalFilename = pIncrementalFilename;
+ return true;
+}
+
+
+bool CIncremental::PrepareForLighting()
+{
+ if( !m_pBSPFilename )
+ return false;
+
+ // Clear the touched faces list.
+ m_FacesTouched.SetSize( numfaces );
+ memset( m_FacesTouched.Base(), 0, numfaces );
+
+ // If we haven't done a complete successful run yet, then we either haven't
+ // loaded the lights, or a run was aborted and our lights are half-done so we
+ // should reload them.
+ if( !m_bSuccessfulRun )
+ LoadIncrementalFile();
+
+ // unmatched = a list of the lights we have
+ CUtlLinkedList<int,int> unmatched;
+ for( int i=m_Lights.Head(); i != m_Lights.InvalidIndex(); i = m_Lights.Next(i) )
+ unmatched.AddToTail( i );
+
+ // Match the light lists and get rid of lights that we already have all the data for.
+ directlight_t *pNext;
+ directlight_t **pPrev = &activelights;
+ for( directlight_t *dl=activelights; dl != NULL; dl = pNext )
+ {
+ pNext = dl->next;
+
+ //float flClosest = 3000000000;
+ //CIncLight *pClosest = 0;
+
+ // Look for this light in our light list.
+ int iNextUnmatched, iUnmatched;
+ for( iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = iNextUnmatched )
+ {
+ iNextUnmatched = unmatched.Next( iUnmatched );
+
+ CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
+
+ //float flTest = (pLight->m_Light.origin - dl->light.origin).Length();
+ //if( flTest < flClosest )
+ //{
+ // flClosest = flTest;
+ // pClosest = pLight;
+ //}
+
+ if( CompareLights( &dl->light, &pLight->m_Light ) )
+ {
+ unmatched.Remove( iUnmatched );
+
+ // Ok, we have this light's data already, yay!
+ // Get rid of it from the active light list.
+ *pPrev = dl->next;
+ free( dl );
+ dl = 0;
+ break;
+ }
+ }
+
+ //bool bTest=false;
+ //if(bTest)
+ // CompareLights( &dl->light, &pClosest->m_Light );
+
+ if( iUnmatched == unmatched.InvalidIndex() )
+ pPrev = &dl->next;
+ }
+
+ // Remove any of our lights that were unmatched.
+ for( int iUnmatched=unmatched.Head(); iUnmatched != unmatched.InvalidIndex(); iUnmatched = unmatched.Next( iUnmatched ) )
+ {
+ CIncLight *pLight = m_Lights[ unmatched[iUnmatched] ];
+
+ // First tag faces that it touched so they get recomposited.
+ for( unsigned short iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
+ {
+ m_FacesTouched[ pLight->m_LightFaces[iFace]->m_FaceIndex ] = 1;
+ }
+
+ delete pLight;
+ m_Lights.Remove( unmatched[iUnmatched] );
+ }
+
+ // Now add a light structure for each new light.
+ AddLightsForActiveLights();
+
+ return true;
+}
+
+
+bool CIncremental::ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader )
+{
+ int version;
+ FileRead( fp, version );
+ if( version != INCREMENTALFILE_VERSION )
+ return false;
+
+ int nFaces;
+ FileRead( fp, nFaces );
+
+ pHeader->m_FaceLightmapSizes.SetSize( nFaces );
+ FileRead( fp, pHeader->m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
+
+ return !FileError();
+}
+
+
+bool CIncremental::WriteIncrementalHeader( long fp )
+{
+ int version = INCREMENTALFILE_VERSION;
+ FileWrite( fp, version );
+
+ int nFaces = numfaces;
+ FileWrite( fp, nFaces );
+
+ CIncrementalHeader hdr;
+ hdr.m_FaceLightmapSizes.SetSize( nFaces );
+
+ for( int i=0; i < nFaces; i++ )
+ {
+ hdr.m_FaceLightmapSizes[i].m_Width = g_pFaces[i].m_LightmapTextureSizeInLuxels[0];
+ hdr.m_FaceLightmapSizes[i].m_Height = g_pFaces[i].m_LightmapTextureSizeInLuxels[1];
+ }
+
+ FileWrite( fp, hdr.m_FaceLightmapSizes.Base(), sizeof(CIncrementalHeader::CLMSize) * nFaces );
+
+ return !FileError();
+}
+
+
+bool CIncremental::IsIncrementalFileValid()
+{
+ long fp = FileOpen( m_pIncrementalFilename, true );
+ if( !fp )
+ return false;
+
+ bool bValid = false;
+ CIncrementalHeader hdr;
+ if( ReadIncrementalHeader( fp, &hdr ) )
+ {
+ // If the number of faces is the same and their lightmap sizes are the same,
+ // then this file is considered a legitimate incremental file.
+ if( hdr.m_FaceLightmapSizes.Count() == numfaces )
+ {
+ int i;
+ for( i=0; i < numfaces; i++ )
+ {
+ if( hdr.m_FaceLightmapSizes[i].m_Width != g_pFaces[i].m_LightmapTextureSizeInLuxels[0] ||
+ hdr.m_FaceLightmapSizes[i].m_Height != g_pFaces[i].m_LightmapTextureSizeInLuxels[1] )
+ {
+ break;
+ }
+ }
+
+ // Were all faces valid?
+ if( i == numfaces )
+ bValid = true;
+ }
+ }
+
+ FileClose( fp );
+ return bValid && !FileError();
+}
+
+
+void CIncremental::AddLightToFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iSample,
+ int lmSize,
+ float dot,
+ int iThread )
+{
+ // If we're not being used, don't do anything.
+ if( !m_pIncrementalFilename )
+ return;
+
+ CIncLight *pLight = m_Lights[lightID];
+
+ // Check for the 99.99% case in which the face already exists.
+ CLightFace *pFace;
+ if( pLight->m_pCachedFaces[iThread] &&
+ pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
+ {
+ pFace = pLight->m_pCachedFaces[iThread];
+ }
+ else
+ {
+ bool bNew;
+
+ EnterCriticalSection( &pLight->m_CS );
+ pFace = pLight->FindOrCreateLightFace( iFace, lmSize, &bNew );
+ LeaveCriticalSection( &pLight->m_CS );
+
+ pLight->m_pCachedFaces[iThread] = pFace;
+
+ if( bNew )
+ m_TotalMemory += pFace->m_LightValues.Count() * sizeof( pFace->m_LightValues[0] );
+ }
+
+ // Add this into the light's data.
+ pFace->m_LightValues[iSample].m_Dot = dot;
+}
+
+
+unsigned short DecodeCharOrShort( CUtlBuffer *pIn )
+{
+ unsigned short val = pIn->GetUnsignedChar();
+ if( val & 0x80 )
+ {
+ val = ((val & 0x7F) << 8) | pIn->GetUnsignedChar();
+ }
+
+ return val;
+}
+
+
+void EncodeCharOrShort( CUtlBuffer *pBuf, unsigned short val )
+{
+ if( (val & 0xFF80) == 0 )
+ {
+ pBuf->PutUnsignedChar( (unsigned char)val );
+ }
+ else
+ {
+ if( val > 32767 )
+ val = 32767;
+
+ pBuf->PutUnsignedChar( (val >> 8) | 0x80 );
+ pBuf->PutUnsignedChar( val & 0xFF );
+ }
+}
+
+
+void DecompressLightData( CUtlBuffer *pIn, CUtlVector<CLightValue> *pOut )
+{
+ int iOut = 0;
+ while( pIn->TellGet() < pIn->TellPut() )
+ {
+ unsigned char runLength = pIn->GetUnsignedChar();
+ unsigned short usVal = DecodeCharOrShort( pIn );
+
+ while( runLength > 0 )
+ {
+ --runLength;
+
+ pOut->Element(iOut).m_Dot = usVal;
+ ++iOut;
+ }
+ }
+}
+
+#ifdef _WIN32
+#pragma warning (disable:4701)
+#endif
+
+void CompressLightData(
+ CLightValue const *pValues,
+ int nValues,
+ CUtlBuffer *pBuf )
+{
+ unsigned char runLength=0;
+ unsigned short flLastValue;
+
+ for( int i=0; i < nValues; i++ )
+ {
+ unsigned short flCurValue = (unsigned short)pValues[i].m_Dot;
+
+ if( i == 0 )
+ {
+ flLastValue = flCurValue;
+ runLength = 1;
+ }
+ else if( flCurValue == flLastValue && runLength < 255 )
+ {
+ ++runLength;
+ }
+ else
+ {
+ pBuf->PutUnsignedChar( runLength );
+ EncodeCharOrShort( pBuf, flLastValue );
+
+ flLastValue = flCurValue;
+ runLength = 1;
+ }
+ }
+
+ // Write the end..
+ if( runLength )
+ {
+ pBuf->PutUnsignedChar( runLength );
+ EncodeCharOrShort( pBuf, flLastValue );
+ }
+}
+
+#ifdef _WIN32
+#pragma warning (default:4701)
+#endif
+
+void MultiplyValues( CUtlVector<CLightValue> &values, float scale )
+{
+ for( int i=0; i < values.Count(); i++ )
+ values[i].m_Dot *= scale;
+}
+
+
+void CIncremental::FinishFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iThread )
+{
+ CIncLight *pLight = m_Lights[lightID];
+
+ // Check for the 99.99% case in which the face already exists.
+ CLightFace *pFace;
+ if( pLight->m_pCachedFaces[iThread] && pLight->m_pCachedFaces[iThread]->m_FaceIndex == iFace )
+ {
+ pFace = pLight->m_pCachedFaces[iThread];
+
+ // Compress the data.
+ MultiplyValues( pFace->m_LightValues, pLight->m_flMaxIntensity );
+
+ pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
+ CompressLightData(
+ pFace->m_LightValues.Base(),
+ pFace->m_LightValues.Count(),
+ &pFace->m_CompressedData );
+
+#if 0
+ // test decompression
+ CUtlVector<CLightValue> test;
+ test.SetSize( 2048 );
+ pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ DecompressLightData( &pFace->m_CompressedData, &test );
+#endif
+
+ if( pFace->m_CompressedData.TellPut() == 0 )
+ {
+ // No contribution.. delete this face from the light.
+ EnterCriticalSection( &pLight->m_CS );
+ pLight->m_LightFaces.Remove( pFace->m_LightFacesIndex );
+ delete pFace;
+ LeaveCriticalSection( &pLight->m_CS );
+ }
+ else
+ {
+ // Discard the uncompressed data.
+ pFace->m_LightValues.Purge();
+ m_FacesTouched[ pFace->m_FaceIndex ] = 1;
+ }
+ }
+}
+
+
+bool CIncremental::Finalize()
+{
+ // If we're not being used, don't do anything.
+ if( !m_pIncrementalFilename || !m_pBSPFilename )
+ return false;
+
+ CUtlVector<CFaceLightList> faceLights;
+ LinkLightsToFaces( faceLights );
+
+ Vector faceLight[(MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2)];
+ CUtlVector<CLightValue> faceLightValues;
+ faceLightValues.SetSize( (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) * (MAX_LIGHTMAP_DIM_WITHOUT_BORDER+2) );
+
+ // Only update the faces we've touched.
+ for( int facenum = 0; facenum < numfaces; facenum++ )
+ {
+ if( !m_FacesTouched[facenum] || !faceLights[facenum].Count() )
+ continue;
+
+ int w = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[0]+1;
+ int h = g_pFaces[facenum].m_LightmapTextureSizeInLuxels[1]+1;
+ int nLuxels = w * h;
+ assert( nLuxels <= sizeof(faceLight) / sizeof(faceLight[0]) );
+
+ // Clear the lighting for this face.
+ memset( faceLight, 0, nLuxels * sizeof(Vector) );
+
+ // Composite all the light contributions.
+ for( int iFace=0; iFace < faceLights[facenum].Count(); iFace++ )
+ {
+ CLightFace *pFace = faceLights[facenum][iFace];
+
+ pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ DecompressLightData( &pFace->m_CompressedData, &faceLightValues );
+
+ for( int iSample=0; iSample < nLuxels; iSample++ )
+ {
+ float flDot = faceLightValues[iSample].m_Dot;
+ if( flDot )
+ {
+ VectorMA(
+ faceLight[iSample],
+ flDot / pFace->m_pLight->m_flMaxIntensity,
+ pFace->m_pLight->m_Light.intensity,
+ faceLight[iSample] );
+ }
+ }
+ }
+
+ // Convert to the floating-point representation in the BSP file.
+ Vector *pSrc = faceLight;
+ unsigned char *pDest = &(*pdlightdata)[ g_pFaces[facenum].lightofs ];
+
+ for( int iSample=0; iSample < nLuxels; iSample++ )
+ {
+ VectorToColorRGBExp32( *pSrc, *( ColorRGBExp32 *)pDest );
+ pDest += 4;
+ pSrc++;
+ }
+ }
+
+ m_bSuccessfulRun = true;
+ return true;
+}
+
+
+void CIncremental::GetFacesTouched( CUtlVector<unsigned char> &touched )
+{
+ touched.CopyArray( m_FacesTouched.Base(), m_FacesTouched.Count() );
+}
+
+
+bool CIncremental::Serialize()
+{
+ if( !SaveIncrementalFile() )
+ return false;
+
+ WriteBSPFile( (char*)m_pBSPFilename );
+ return true;
+}
+
+
+void CIncremental::Term()
+{
+ m_Lights.PurgeAndDeleteElements();
+ m_TotalMemory = 0;
+}
+
+
+void CIncremental::AddLightsForActiveLights()
+{
+ // Create our lights.
+ for( directlight_t *dl=activelights; dl != NULL; dl = dl->next )
+ {
+ CIncLight *pLight = new CIncLight;
+ dl->m_IncrementalID = m_Lights.AddToTail( pLight );
+
+ // Copy the light information.
+ pLight->m_Light = dl->light;
+ pLight->m_flMaxIntensity = max( dl->light.intensity[0], max( dl->light.intensity[1], dl->light.intensity[2] ) );
+ }
+}
+
+
+bool CIncremental::LoadIncrementalFile()
+{
+ Term();
+
+ if( !IsIncrementalFileValid() )
+ return false;
+
+ long fp = FileOpen( m_pIncrementalFilename, true );
+ if( !fp )
+ return false;
+
+ // Read the header.
+ CIncrementalHeader hdr;
+ if( !ReadIncrementalHeader( fp, &hdr ) )
+ {
+ FileClose( fp );
+ return false;
+ }
+
+
+ // Read the lights.
+ int nLights;
+ FileRead( fp, nLights );
+ for( int iLight=0; iLight < nLights; iLight++ )
+ {
+ CIncLight *pLight = new CIncLight;
+ m_Lights.AddToTail( pLight );
+
+ FileRead( fp, pLight->m_Light );
+ pLight->m_flMaxIntensity =
+ max( pLight->m_Light.intensity.x,
+ max( pLight->m_Light.intensity.y, pLight->m_Light.intensity.z ) );
+
+ int nFaces;
+ FileRead( fp, nFaces );
+ assert( nFaces < 70000 );
+
+ for( int iFace=0; iFace < nFaces; iFace++ )
+ {
+ CLightFace *pFace = new CLightFace;
+ pLight->m_LightFaces.AddToTail( pFace );
+
+ pFace->m_pLight = pLight;
+ FileRead( fp, pFace->m_FaceIndex );
+
+ int dataSize;
+ FileRead( fp, dataSize );
+
+ pFace->m_CompressedData.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
+ while( dataSize )
+ {
+ --dataSize;
+
+ unsigned char ucData;
+ FileRead( fp, ucData );
+
+ pFace->m_CompressedData.PutUnsignedChar( ucData );
+ }
+ }
+ }
+
+
+ FileClose( fp );
+ return !FileError();
+}
+
+
+bool CIncremental::SaveIncrementalFile()
+{
+ long fp = FileOpen( m_pIncrementalFilename, false );
+ if( !fp )
+ return false;
+
+ if( !WriteIncrementalHeader( fp ) )
+ {
+ FileClose( fp );
+ return false;
+ }
+
+ // Write the lights.
+ int nLights = m_Lights.Count();
+ FileWrite( fp, nLights );
+ for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
+ {
+ CIncLight *pLight = m_Lights[iLight];
+
+ FileWrite( fp, pLight->m_Light );
+
+ int nFaces = pLight->m_LightFaces.Count();
+ FileWrite( fp, nFaces );
+ for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
+ {
+ CLightFace *pFace = pLight->m_LightFaces[iFace];
+
+ FileWrite( fp, pFace->m_FaceIndex );
+
+ int dataSize = pFace->m_CompressedData.TellPut();
+ FileWrite( fp, dataSize );
+
+ pFace->m_CompressedData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ while( dataSize )
+ {
+ --dataSize;
+ FileWrite( fp, pFace->m_CompressedData.GetUnsignedChar() );
+ }
+ }
+ }
+
+
+ FileClose( fp );
+ return !FileError();
+}
+
+
+void CIncremental::LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights )
+{
+ faceLights.SetSize( numfaces );
+
+ for( int iLight=m_Lights.Head(); iLight != m_Lights.InvalidIndex(); iLight = m_Lights.Next( iLight ) )
+ {
+ CIncLight *pLight = m_Lights[iLight];
+
+ for( int iFace=pLight->m_LightFaces.Head(); iFace != pLight->m_LightFaces.InvalidIndex(); iFace = pLight->m_LightFaces.Next( iFace ) )
+ {
+ CLightFace *pFace = pLight->m_LightFaces[iFace];
+
+ if( m_FacesTouched[pFace->m_FaceIndex] )
+ faceLights[ pFace->m_FaceIndex ].AddToTail( pFace );
+ }
+ }
+}
+
+
+// ------------------------------------------------------------------ //
+// CIncLight
+// ------------------------------------------------------------------ //
+
+CIncLight::CIncLight()
+{
+ memset( m_pCachedFaces, 0, sizeof(m_pCachedFaces) );
+ InitializeCriticalSection( &m_CS );
+}
+
+
+CIncLight::~CIncLight()
+{
+ m_LightFaces.PurgeAndDeleteElements();
+ DeleteCriticalSection( &m_CS );
+}
+
+
+CLightFace* CIncLight::FindOrCreateLightFace( int iFace, int lmSize, bool *bNew )
+{
+ if( bNew )
+ *bNew = false;
+
+
+ // Look for it.
+ for( int i=m_LightFaces.Head(); i != m_LightFaces.InvalidIndex(); i=m_LightFaces.Next(i) )
+ {
+ CLightFace *pFace = m_LightFaces[i];
+
+ if( pFace->m_FaceIndex == iFace )
+ {
+ assert( pFace->m_LightValues.Count() == lmSize );
+ return pFace;
+ }
+ }
+
+ // Ok, create one.
+ CLightFace *pFace = new CLightFace;
+ pFace->m_LightFacesIndex = m_LightFaces.AddToTail( pFace );
+ pFace->m_pLight = this;
+
+ pFace->m_FaceIndex = iFace;
+ pFace->m_LightValues.SetSize( lmSize );
+ memset( pFace->m_LightValues.Base(), 0, sizeof( CLightValue ) * lmSize );
+
+ if( bNew )
+ *bNew = true;
+
+ return pFace;
+}
+
+
diff --git a/mp/src/utils/vrad/incremental.h b/mp/src/utils/vrad/incremental.h new file mode 100644 index 00000000..fdac144e --- /dev/null +++ b/mp/src/utils/vrad/incremental.h @@ -0,0 +1,175 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef INCREMENTAL_H
+#define INCREMENTAL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+
+#include "iincremental.h"
+#include "utllinkedlist.h"
+#include "utlvector.h"
+#include "utlbuffer.h"
+#include "vrad.h"
+
+
+#define INCREMENTALFILE_VERSION 31241
+
+
+class CIncLight;
+
+
+class CLightValue
+{
+public:
+ float m_Dot;
+};
+
+
+class CLightFace
+{
+public:
+ unsigned short m_FaceIndex; // global face index
+ unsigned short m_LightFacesIndex; // index into CIncLight::m_LightFaces.
+
+ // The lightmap grid for this face. Only used while building lighting data for a face.
+ // Compressed into m_CompressedData immediately afterwards.
+ CUtlVector<CLightValue> m_LightValues;
+
+ CUtlBuffer m_CompressedData;
+ CIncLight *m_pLight;
+};
+
+
+class CIncLight
+{
+public:
+ CIncLight();
+ ~CIncLight();
+
+ CLightFace* FindOrCreateLightFace( int iFace, int lmSize, bool *bNew=NULL );
+
+
+public:
+
+ CRITICAL_SECTION m_CS;
+
+ // This is the light for which m_LightFaces was built.
+ dworldlight_t m_Light;
+
+ CLightFace *m_pCachedFaces[MAX_TOOL_THREADS+1];
+
+ // The list of faces that this light contributes to.
+ CUtlLinkedList<CLightFace*, unsigned short> m_LightFaces;
+
+ // Largest value in intensity of light. Used to scale dot products up into a
+ // range where their values make sense.
+ float m_flMaxIntensity;
+};
+
+
+class CIncrementalHeader
+{
+public:
+ class CLMSize
+ {
+ public:
+ unsigned char m_Width;
+ unsigned char m_Height;
+ };
+
+ CUtlVector<CLMSize> m_FaceLightmapSizes;
+};
+
+
+class CIncremental : public IIncremental
+{
+public:
+
+ CIncremental();
+ ~CIncremental();
+
+
+
+// IIncremental overrides.
+public:
+
+ virtual bool Init( char const *pBSPFilename, char const *pIncrementalFilename );
+
+ // Load the light definitions out of the incremental file.
+ // Figure out which lights have changed.
+ // Change 'activelights' to only consist of new or changed lights.
+ virtual bool PrepareForLighting();
+
+ virtual void AddLightToFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iSample,
+ int lmSize,
+ float dot,
+ int iThread );
+
+ virtual void FinishFace(
+ IncrementalLightID lightID,
+ int iFace,
+ int iThread );
+
+ // For each face that was changed during the lighting process, save out
+ // new data for it in the incremental file.
+ virtual bool Finalize();
+
+ virtual void GetFacesTouched( CUtlVector<unsigned char> &touched );
+
+ virtual bool Serialize();
+
+
+private:
+
+ // Read/write the header from the file.
+ bool ReadIncrementalHeader( long fp, CIncrementalHeader *pHeader );
+ bool WriteIncrementalHeader( long fp );
+
+ // Returns true if the incremental file is valid and we can use InitUpdate.
+ bool IsIncrementalFileValid();
+
+ void Term();
+
+ // For each light in 'activelights', add a light to m_Lights and link them together.
+ void AddLightsForActiveLights();
+
+ // Load and save the state.
+ bool LoadIncrementalFile();
+ bool SaveIncrementalFile();
+
+ typedef CUtlVector<CLightFace*> CFaceLightList;
+ void LinkLightsToFaces( CUtlVector<CFaceLightList> &faceLights );
+
+
+private:
+
+ char const *m_pIncrementalFilename;
+ char const *m_pBSPFilename;
+
+ CUtlLinkedList<CIncLight*, IncrementalLightID>
+ m_Lights;
+
+ // The face index is set to 1 if a face has new lighting data applied to it.
+ // This is used to optimize the set of lightmaps we recomposite.
+ CUtlVector<unsigned char> m_FacesTouched;
+
+ int m_TotalMemory;
+
+ // Set to true when one or more runs were completed successfully.
+ bool m_bSuccessfulRun;
+};
+
+
+
+#endif // INCREMENTAL_H
diff --git a/mp/src/utils/vrad/leaf_ambient_lighting.cpp b/mp/src/utils/vrad/leaf_ambient_lighting.cpp new file mode 100644 index 00000000..ea26c8c6 --- /dev/null +++ b/mp/src/utils/vrad/leaf_ambient_lighting.cpp @@ -0,0 +1,708 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "leaf_ambient_lighting.h"
+#include "bsplib.h"
+#include "vraddetailprops.h"
+#include "mathlib/anorms.h"
+#include "pacifier.h"
+#include "coordsize.h"
+#include "vstdlib/random.h"
+#include "bsptreedata.h"
+#include "messbuf.h"
+#include "vmpi.h"
+#include "vmpi_distribute_work.h"
+
+static TableVector g_BoxDirections[6] =
+{
+ { 1, 0, 0 },
+ { -1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, -1, 0 },
+ { 0, 0, 1 },
+ { 0, 0, -1 },
+};
+
+
+
+static void ComputeAmbientFromSurface( dface_t *surfID, dworldlight_t* pSkylight,
+ Vector& radcolor )
+{
+ if ( !surfID )
+ return;
+
+ texinfo_t *pTexInfo = &texinfo[surfID->texinfo];
+
+ // If we hit the sky, use the sky ambient
+ if ( pTexInfo->flags & SURF_SKY )
+ {
+ if ( pSkylight )
+ {
+ // add in sky ambient
+ VectorCopy( pSkylight->intensity, radcolor );
+ }
+ }
+ else
+ {
+ Vector reflectivity = dtexdata[pTexInfo->texdata].reflectivity;
+ VectorMultiply( radcolor, reflectivity, radcolor );
+ }
+}
+
+
+// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
+float Engine_WorldLightAngle( const dworldlight_t *wl, const Vector& lnormal, const Vector& snormal, const Vector& delta )
+{
+ float dot, dot2;
+
+ Assert( wl->type == emit_surface );
+
+ dot = DotProduct( snormal, delta );
+ if (dot < 0)
+ return 0;
+
+ dot2 = -DotProduct (delta, lnormal);
+ if (dot2 <= ON_EPSILON/10)
+ return 0; // behind light surface
+
+ return dot * dot2;
+}
+
+
+// TODO: it's CRAZY how much lighting code we share with the engine. It should all be shared code.
+float Engine_WorldLightDistanceFalloff( const dworldlight_t *wl, const Vector& delta )
+{
+ Assert( wl->type == emit_surface );
+
+ // Cull out stuff that's too far
+ if (wl->radius != 0)
+ {
+ if ( DotProduct( delta, delta ) > (wl->radius * wl->radius))
+ return 0.0f;
+ }
+
+ return InvRSquared(delta);
+}
+
+
+void AddEmitSurfaceLights( const Vector &vStart, Vector lightBoxColor[6] )
+{
+ fltx4 fractionVisible;
+
+ FourVectors vStart4, wlOrigin4;
+ vStart4.DuplicateVector ( vStart );
+
+ for ( int iLight=0; iLight < *pNumworldlights; iLight++ )
+ {
+ dworldlight_t *wl = &dworldlights[iLight];
+
+ // Should this light even go in the ambient cubes?
+ if ( !( wl->flags & DWL_FLAGS_INAMBIENTCUBE ) )
+ continue;
+
+ Assert( wl->type == emit_surface );
+
+ // Can this light see the point?
+ wlOrigin4.DuplicateVector ( wl->origin );
+ TestLine ( vStart4, wlOrigin4, &fractionVisible );
+ if ( !TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
+ continue;
+
+ // Add this light's contribution.
+ Vector vDelta = wl->origin - vStart;
+ float flDistanceScale = Engine_WorldLightDistanceFalloff( wl, vDelta );
+
+ Vector vDeltaNorm = vDelta;
+ VectorNormalize( vDeltaNorm );
+ float flAngleScale = Engine_WorldLightAngle( wl, wl->normal, vDeltaNorm, vDeltaNorm );
+
+ float ratio = flDistanceScale * flAngleScale * SubFloat ( fractionVisible, 0 );
+ if ( ratio == 0 )
+ continue;
+
+ for ( int i=0; i < 6; i++ )
+ {
+ float t = DotProduct( g_BoxDirections[i], vDeltaNorm );
+ if ( t > 0 )
+ {
+ lightBoxColor[i] += wl->intensity * (t * ratio);
+ }
+ }
+ }
+}
+
+
+void ComputeAmbientFromSphericalSamples( int iThread, const Vector &vStart, Vector lightBoxColor[6] )
+{
+ // Figure out the color that rays hit when shot out from this position.
+ Vector radcolor[NUMVERTEXNORMALS];
+ float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
+
+ for ( int i = 0; i < NUMVERTEXNORMALS; i++ )
+ {
+ Vector vEnd = vStart + g_anorms[i] * (COORD_EXTENT * 1.74);
+
+ // Now that we've got a ray, see what surface we've hit
+ Vector lightStyleColors[MAX_LIGHTSTYLES];
+ lightStyleColors[0].Init(); // We only care about light style 0 here.
+ CalcRayAmbientLighting( iThread, vStart, vEnd, tanTheta, lightStyleColors );
+
+ radcolor[i] = lightStyleColors[0];
+ }
+
+ // accumulate samples into radiant box
+ for ( int j = 6; --j >= 0; )
+ {
+ float t = 0;
+
+ lightBoxColor[j].Init();
+
+ for (int i = 0; i < NUMVERTEXNORMALS; i++)
+ {
+ float c = DotProduct( g_anorms[i], g_BoxDirections[j] );
+ if (c > 0)
+ {
+ t += c;
+ lightBoxColor[j] += radcolor[i] * c;
+ }
+ }
+
+ lightBoxColor[j] *= 1/t;
+ }
+
+ // Now add direct light from the emit_surface lights. These go in the ambient cube because
+ // there are a ton of them and they are often so dim that they get filtered out by r_worldlightmin.
+ AddEmitSurfaceLights( vStart, lightBoxColor );
+}
+
+
+bool IsLeafAmbientSurfaceLight( dworldlight_t *wl )
+{
+ static const float g_flWorldLightMinEmitSurface = 0.005f;
+ static const float g_flWorldLightMinEmitSurfaceDistanceRatio = ( InvRSquared( Vector( 0, 0, 512 ) ) );
+
+ if ( wl->type != emit_surface )
+ return false;
+
+ if ( wl->style != 0 )
+ return false;
+
+ float intensity = max( wl->intensity[0], wl->intensity[1] );
+ intensity = max( intensity, wl->intensity[2] );
+
+ return (intensity * g_flWorldLightMinEmitSurfaceDistanceRatio) < g_flWorldLightMinEmitSurface;
+}
+
+
+class CLeafSampler
+{
+public:
+ CLeafSampler( int iThread ) : m_iThread(iThread) {}
+
+ // Generate a random point in the leaf's bounding volume
+ // reject any points that aren't actually in the leaf
+ // do a couple of tracing heuristics to eliminate points that are inside detail brushes
+ // or underneath displacement surfaces in the leaf
+ // return once we have a valid point, use the center if one can't be computed quickly
+ void GenerateLeafSamplePosition( int leafIndex, const CUtlVector<dplane_t> &leafPlanes, Vector &samplePosition )
+ {
+ dleaf_t *pLeaf = dleafs + leafIndex;
+
+ float dx = pLeaf->maxs[0] - pLeaf->mins[0];
+ float dy = pLeaf->maxs[1] - pLeaf->mins[1];
+ float dz = pLeaf->maxs[2] - pLeaf->mins[2];
+ bool bValid = false;
+ for ( int i = 0; i < 1000 && !bValid; i++ )
+ {
+ samplePosition.x = pLeaf->mins[0] + m_random.RandomFloat(0, dx);
+ samplePosition.y = pLeaf->mins[1] + m_random.RandomFloat(0, dy);
+ samplePosition.z = pLeaf->mins[2] + m_random.RandomFloat(0, dz);
+ bValid = true;
+
+ for ( int j = leafPlanes.Count(); --j >= 0 && bValid; )
+ {
+ float d = DotProduct(leafPlanes[j].normal, samplePosition) - leafPlanes[j].dist;
+ if ( d < DIST_EPSILON )
+ {
+ // not inside the leaf, try again
+ bValid = false;
+ break;
+ }
+ }
+ if ( !bValid )
+ continue;
+
+ for ( int j = 0; j < 6; j++ )
+ {
+ Vector start = samplePosition;
+ int axis = j%3;
+ start[axis] = (j<3) ? pLeaf->mins[axis] : pLeaf->maxs[axis];
+ float t;
+ Vector normal;
+ CastRayInLeaf( m_iThread, samplePosition, start, leafIndex, &t, &normal );
+ if ( t == 0.0f )
+ {
+ // inside a func_detail, try again.
+ bValid = false;
+ break;
+ }
+ if ( t != 1.0f )
+ {
+ Vector delta = start - samplePosition;
+ if ( DotProduct(delta, normal) > 0 )
+ {
+ // hit backside of displacement, try again.
+ bValid = false;
+ break;
+ }
+ }
+ }
+ }
+ if ( !bValid )
+ {
+ // didn't generate a valid sample point, just use the center of the leaf bbox
+ samplePosition = ( Vector( pLeaf->mins[0], pLeaf->mins[1], pLeaf->mins[2] ) + Vector( pLeaf->maxs[0], pLeaf->maxs[1], pLeaf->maxs[2] ) ) * 0.5f;
+ }
+ }
+
+private:
+ int m_iThread;
+ CUniformRandomStream m_random;
+};
+
+// gets a list of the planes pointing into a leaf
+void GetLeafBoundaryPlanes( CUtlVector<dplane_t> &list, int leafIndex )
+{
+ list.RemoveAll();
+ int nodeIndex = leafparents[leafIndex];
+ int child = -(leafIndex + 1);
+ while ( nodeIndex >= 0 )
+ {
+ dnode_t *pNode = dnodes + nodeIndex;
+ dplane_t *pNodePlane = dplanes + pNode->planenum;
+ if ( pNode->children[0] == child )
+ {
+ // front side
+ list.AddToTail( *pNodePlane );
+ }
+ else
+ {
+ // back side
+ int plane = list.AddToTail();
+ list[plane].dist = -pNodePlane->dist;
+ list[plane].normal = -pNodePlane->normal;
+ list[plane].type = pNodePlane->type;
+ }
+ child = nodeIndex;
+ nodeIndex = nodeparents[child];
+ }
+}
+
+// this stores each sample of the ambient lighting
+struct ambientsample_t
+{
+ Vector pos;
+ Vector cube[6];
+};
+
+// add the sample to the list. If we exceed the maximum number of samples, the worst sample will
+// be discarded. This has the effect of converging on the best samples when enough are added.
+void AddSampleToList( CUtlVector<ambientsample_t> &list, const Vector &samplePosition, Vector *pCube )
+{
+ const int MAX_SAMPLES = 16;
+
+ int index = list.AddToTail();
+ list[index].pos = samplePosition;
+ for ( int i = 0; i < 6; i++ )
+ {
+ list[index].cube[i] = pCube[i];
+ }
+
+ if ( list.Count() <= MAX_SAMPLES )
+ return;
+
+ int nearestNeighborIndex = 0;
+ float nearestNeighborDist = FLT_MAX;
+ float nearestNeighborTotal = 0;
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ int closestIndex = 0;
+ float closestDist = FLT_MAX;
+ float totalDC = 0;
+ for ( int j = 0; j < list.Count(); j++ )
+ {
+ if ( j == i )
+ continue;
+ float dist = (list[i].pos - list[j].pos).Length();
+ float maxDC = 0;
+ for ( int k = 0; k < 6; k++ )
+ {
+ // color delta is computed per-component, per cube side
+ for (int s = 0; s < 3; s++ )
+ {
+ float dc = fabs(list[i].cube[k][s] - list[j].cube[k][s]);
+ maxDC = max(maxDC,dc);
+ }
+ totalDC += maxDC;
+ }
+ // need a measurable difference in color or we'll just rely on position
+ if ( maxDC < 1e-4f )
+ {
+ maxDC = 0;
+ }
+ else if ( maxDC > 1.0f )
+ {
+ maxDC = 1.0f;
+ }
+ // selection criteria is 10% distance, 90% color difference
+ // choose samples that fill the space (large distance from each other)
+ // and have largest color variation
+ float distanceFactor = 0.1f + (maxDC * 0.9f);
+ dist *= distanceFactor;
+
+ // find the "closest" sample to this one
+ if ( dist < closestDist )
+ {
+ closestDist = dist;
+ closestIndex = j;
+ }
+ }
+ // the sample with the "closest" neighbor is rejected
+ if ( closestDist < nearestNeighborDist || (closestDist == nearestNeighborDist && totalDC < nearestNeighborTotal) )
+ {
+ nearestNeighborDist = closestDist;
+ nearestNeighborIndex = i;
+ }
+ }
+ list.FastRemove( nearestNeighborIndex );
+}
+
+// max number of units in gamma space of per-side delta
+int CubeDeltaGammaSpace( Vector *pCube0, Vector *pCube1 )
+{
+ int maxDelta = 0;
+ // do this comparison in gamma space to try and get a perceptual basis for the compare
+ for ( int i = 0; i < 6; i++ )
+ {
+ for ( int j = 0; j < 3; j++ )
+ {
+ int val0 = LinearToScreenGamma( pCube0[i][j] );
+ int val1 = LinearToScreenGamma( pCube1[i][j] );
+ int delta = abs(val0-val1);
+ if ( delta > maxDelta )
+ maxDelta = delta;
+ }
+ }
+ return maxDelta;
+}
+// reconstruct the ambient lighting for a leaf at the given position in worldspace
+// optionally skip one of the entries in the list
+void Mod_LeafAmbientColorAtPos( Vector *pOut, const Vector &pos, const CUtlVector<ambientsample_t> &list, int skipIndex )
+{
+ for ( int i = 0; i < 6; i++ )
+ {
+ pOut[i].Init();
+ }
+ float totalFactor = 0;
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ if ( i == skipIndex )
+ continue;
+ // do an inverse squared distance weighted average of the samples to reconstruct
+ // the original function
+ float dist = (list[i].pos - pos).LengthSqr();
+ float factor = 1.0f / (dist + 1.0f);
+ totalFactor += factor;
+ for ( int j = 0; j < 6; j++ )
+ {
+ pOut[j] += list[i].cube[j] * factor;
+ }
+ }
+ for ( int i = 0; i < 6; i++ )
+ {
+ pOut[i] *= (1.0f / totalFactor);
+ }
+}
+
+// this samples the lighting at each sample and removes any unnecessary samples
+void CompressAmbientSampleList( CUtlVector<ambientsample_t> &list )
+{
+ Vector testCube[6];
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ if ( list.Count() > 1 )
+ {
+ Mod_LeafAmbientColorAtPos( testCube, list[i].pos, list, i );
+ if ( CubeDeltaGammaSpace(testCube, list[i].cube) < 3 )
+ {
+ list.FastRemove(i);
+ i--;
+ }
+ }
+ }
+}
+
+// basically this is an intersection routine that returns a distance between the boxes
+float AABBDistance( const Vector &mins0, const Vector &maxs0, const Vector &mins1, const Vector &maxs1 )
+{
+ Vector delta;
+ for ( int i = 0; i < 3; i++ )
+ {
+ float greatestMin = max(mins0[i], mins1[i]);
+ float leastMax = min(maxs0[i], maxs1[i]);
+ delta[i] = (greatestMin < leastMax) ? 0 : (leastMax - greatestMin);
+ }
+ return delta.Length();
+}
+
+// build a list of leaves from a query
+class CLeafList : public ISpatialLeafEnumerator
+{
+public:
+ virtual bool EnumerateLeaf( int leaf, int context )
+ {
+ m_list.AddToTail(leaf);
+ return true;
+ }
+
+ CUtlVector<int> m_list;
+};
+
+// conver short[3] to vector
+static void LeafBounds( int leafIndex, Vector &mins, Vector &maxs )
+{
+ for ( int i = 0; i < 3; i++ )
+ {
+ mins[i] = dleafs[leafIndex].mins[i];
+ maxs[i] = dleafs[leafIndex].maxs[i];
+ }
+}
+
+// returns the index of the nearest leaf with ambient samples
+int NearestNeighborWithLight(int leafID)
+{
+ Vector mins, maxs;
+ LeafBounds( leafID, mins, maxs );
+ Vector size = maxs - mins;
+ CLeafList leafList;
+ ToolBSPTree()->EnumerateLeavesInBox( mins-size, maxs+size, &leafList, 0 );
+ float bestDist = FLT_MAX;
+ int bestIndex = leafID;
+ for ( int i = 0; i < leafList.m_list.Count(); i++ )
+ {
+ int testIndex = leafList.m_list[i];
+ if ( !g_pLeafAmbientIndex->Element(testIndex).ambientSampleCount )
+ continue;
+
+ Vector testMins, testMaxs;
+ LeafBounds( testIndex, testMins, testMaxs );
+ float dist = AABBDistance( mins, maxs, testMins, testMaxs );
+ if ( dist < bestDist )
+ {
+ bestDist = dist;
+ bestIndex = testIndex;
+ }
+ }
+ return bestIndex;
+}
+
+// maps a float to a byte fraction between min & max
+static byte Fixed8Fraction( float t, float tMin, float tMax )
+{
+ if ( tMax <= tMin )
+ return 0;
+
+ float frac = RemapValClamped( t, tMin, tMax, 0.0f, 255.0f );
+ return byte(frac+0.5f);
+}
+
+CUtlVector< CUtlVector<ambientsample_t> > g_LeafAmbientSamples;
+
+void ComputeAmbientForLeaf( int iThread, int leafID, CUtlVector<ambientsample_t> &list )
+{
+ CUtlVector<dplane_t> leafPlanes;
+ CLeafSampler sampler( iThread );
+
+ GetLeafBoundaryPlanes( leafPlanes, leafID );
+ list.RemoveAll();
+ // this heuristic tries to generate at least one sample per volume (chosen to be similar to the size of a player) in the space
+ int xSize = (dleafs[leafID].maxs[0] - dleafs[leafID].mins[0]) / 32;
+ int ySize = (dleafs[leafID].maxs[1] - dleafs[leafID].mins[1]) / 32;
+ int zSize = (dleafs[leafID].maxs[2] - dleafs[leafID].mins[2]) / 64;
+ xSize = max(xSize,1);
+ ySize = max(xSize,1);
+ zSize = max(xSize,1);
+ // generate update 128 candidate samples, always at least one sample
+ int volumeCount = xSize * ySize * zSize;
+ if ( g_bFastAmbient )
+ {
+ // save compute time, only do one sample
+ volumeCount = 1;
+ }
+ int sampleCount = clamp( volumeCount, 1, 128 );
+ if ( dleafs[leafID].contents & CONTENTS_SOLID )
+ {
+ // don't generate any samples in solid leaves
+ // NOTE: We copy the nearest non-solid leaf sample pointers into this leaf at the end
+ return;
+ }
+ Vector cube[6];
+ for ( int i = 0; i < sampleCount; i++ )
+ {
+ // compute each candidate sample and add to the list
+ Vector samplePosition;
+ sampler.GenerateLeafSamplePosition( leafID, leafPlanes, samplePosition );
+ ComputeAmbientFromSphericalSamples( iThread, samplePosition, cube );
+ // note this will remove the least valuable sample once the limit is reached
+ AddSampleToList( list, samplePosition, cube );
+ }
+
+ // remove any samples that can be reconstructed with the remaining data
+ CompressAmbientSampleList( list );
+}
+
+static void ThreadComputeLeafAmbient( int iThread, void *pUserData )
+{
+ CUtlVector<ambientsample_t> list;
+ while (1)
+ {
+ int leafID = GetThreadWork ();
+ if (leafID == -1)
+ break;
+ list.RemoveAll();
+ ComputeAmbientForLeaf(iThread, leafID, list);
+ // copy to the output array
+ g_LeafAmbientSamples[leafID].SetCount( list.Count() );
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ g_LeafAmbientSamples[leafID].Element(i) = list.Element(i);
+ }
+ }
+}
+
+void VMPI_ProcessLeafAmbient( int iThread, uint64 iLeaf, MessageBuffer *pBuf )
+{
+ CUtlVector<ambientsample_t> list;
+ ComputeAmbientForLeaf(iThread, (int)iLeaf, list);
+
+ VMPI_SetCurrentStage( "EncodeLeafAmbientResults" );
+
+ // Encode the results.
+ int nSamples = list.Count();
+ pBuf->write( &nSamples, sizeof( nSamples ) );
+ if ( nSamples )
+ {
+ pBuf->write( list.Base(), list.Count() * sizeof( ambientsample_t ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Called on the master when a worker finishes processing a static prop.
+//-----------------------------------------------------------------------------
+void VMPI_ReceiveLeafAmbientResults( uint64 leafID, MessageBuffer *pBuf, int iWorker )
+{
+ // Decode the results.
+ int nSamples;
+ pBuf->read( &nSamples, sizeof( nSamples ) );
+
+ g_LeafAmbientSamples[leafID].SetCount( nSamples );
+ if ( nSamples )
+ {
+ pBuf->read(g_LeafAmbientSamples[leafID].Base(), nSamples * sizeof(ambientsample_t) );
+ }
+}
+
+
+void ComputePerLeafAmbientLighting()
+{
+ // Figure out which lights should go in the per-leaf ambient cubes.
+ int nInAmbientCube = 0;
+ int nSurfaceLights = 0;
+ for ( int i=0; i < *pNumworldlights; i++ )
+ {
+ dworldlight_t *wl = &dworldlights[i];
+
+ if ( IsLeafAmbientSurfaceLight( wl ) )
+ wl->flags |= DWL_FLAGS_INAMBIENTCUBE;
+ else
+ wl->flags &= ~DWL_FLAGS_INAMBIENTCUBE;
+
+ if ( wl->type == emit_surface )
+ ++nSurfaceLights;
+
+ if ( wl->flags & DWL_FLAGS_INAMBIENTCUBE )
+ ++nInAmbientCube;
+ }
+
+ Msg( "%d of %d (%d%% of) surface lights went in leaf ambient cubes.\n", nInAmbientCube, nSurfaceLights, nSurfaceLights ? ((nInAmbientCube*100) / nSurfaceLights) : 0 );
+
+ g_LeafAmbientSamples.SetCount(numleafs);
+
+ if ( g_bUseMPI )
+ {
+ // Distribute the work among the workers.
+ VMPI_SetCurrentStage( "ComputeLeafAmbientLighting" );
+ DistributeWork( numleafs, VMPI_DISTRIBUTEWORK_PACKETID, VMPI_ProcessLeafAmbient, VMPI_ReceiveLeafAmbientResults );
+ }
+ else
+ {
+ RunThreadsOn(numleafs, true, ThreadComputeLeafAmbient);
+ }
+
+ // now write out the data
+ Msg("Writing leaf ambient...");
+ g_pLeafAmbientIndex->RemoveAll();
+ g_pLeafAmbientLighting->RemoveAll();
+ g_pLeafAmbientIndex->SetCount( numleafs );
+ g_pLeafAmbientLighting->EnsureCapacity( numleafs*4 );
+ for ( int leafID = 0; leafID < numleafs; leafID++ )
+ {
+ const CUtlVector<ambientsample_t> &list = g_LeafAmbientSamples[leafID];
+ g_pLeafAmbientIndex->Element(leafID).ambientSampleCount = list.Count();
+ if ( !list.Count() )
+ {
+ g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = 0;
+ }
+ else
+ {
+ g_pLeafAmbientIndex->Element(leafID).firstAmbientSample = g_pLeafAmbientLighting->Count();
+ // compute the samples in disk format. Encode the positions in 8-bits using leaf bounds fractions
+ for ( int i = 0; i < list.Count(); i++ )
+ {
+ int outIndex = g_pLeafAmbientLighting->AddToTail();
+ dleafambientlighting_t &light = g_pLeafAmbientLighting->Element(outIndex);
+
+ light.x = Fixed8Fraction( list[i].pos.x, dleafs[leafID].mins[0], dleafs[leafID].maxs[0] );
+ light.y = Fixed8Fraction( list[i].pos.y, dleafs[leafID].mins[1], dleafs[leafID].maxs[1] );
+ light.z = Fixed8Fraction( list[i].pos.z, dleafs[leafID].mins[2], dleafs[leafID].maxs[2] );
+ light.pad = 0;
+ for ( int side = 0; side < 6; side++ )
+ {
+ VectorToColorRGBExp32( list[i].cube[side], light.cube.m_Color[side] );
+ }
+ }
+ }
+ }
+ for ( int i = 0; i < numleafs; i++ )
+ {
+ // UNDONE: Do this dynamically in the engine instead. This will allow us to sample across leaf
+ // boundaries always which should improve the quality of lighting in general
+ if ( g_pLeafAmbientIndex->Element(i).ambientSampleCount == 0 )
+ {
+ if ( !(dleafs[i].contents & CONTENTS_SOLID) )
+ {
+ Msg("Bad leaf ambient for leaf %d\n", i );
+ }
+
+ int refLeaf = NearestNeighborWithLight(i);
+ g_pLeafAmbientIndex->Element(i).ambientSampleCount = 0;
+ g_pLeafAmbientIndex->Element(i).firstAmbientSample = refLeaf;
+ }
+ }
+ Msg("done\n");
+}
+
diff --git a/mp/src/utils/vrad/leaf_ambient_lighting.h b/mp/src/utils/vrad/leaf_ambient_lighting.h new file mode 100644 index 00000000..b6bb50eb --- /dev/null +++ b/mp/src/utils/vrad/leaf_ambient_lighting.h @@ -0,0 +1,17 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef LEAF_AMBIENT_LIGHTING_H
+#define LEAF_AMBIENT_LIGHTING_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void ComputePerLeafAmbientLighting();
+
+
+#endif // LEAF_AMBIENT_LIGHTING_H
diff --git a/mp/src/utils/vrad/lightmap.cpp b/mp/src/utils/vrad/lightmap.cpp new file mode 100644 index 00000000..b6f8c1f7 --- /dev/null +++ b/mp/src/utils/vrad/lightmap.cpp @@ -0,0 +1,3576 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "lightmap.h"
+#include "radial.h"
+#include "mathlib/bumpvects.h"
+#include "tier1/utlvector.h"
+#include "vmpi.h"
+#include "mathlib/anorms.h"
+#include "map_utils.h"
+#include "mathlib/halton.h"
+#include "imagepacker.h"
+#include "tier1/utlrbtree.h"
+#include "tier1/utlbuffer.h"
+#include "bitmap/tgawriter.h"
+#include "mathlib/quantize.h"
+#include "bitmap/imageformat.h"
+#include "coordsize.h"
+
+enum
+{
+ AMBIENT_ONLY = 0x1,
+ NON_AMBIENT_ONLY = 0x2,
+};
+
+#define SMOOTHING_GROUP_HARD_EDGE 0xff000000
+
+//==========================================================================//
+// CNormalList.
+//==========================================================================//
+
+// This class keeps a list of unique normals and provides a fast
+class CNormalList
+{
+public:
+ CNormalList();
+
+ // Adds the normal if unique. Otherwise, returns the normal's index into m_Normals.
+ int FindOrAddNormal( Vector const &vNormal );
+
+
+public:
+
+ CUtlVector<Vector> m_Normals;
+
+
+private:
+
+ // This represents a grid from (-1,-1,-1) to (1,1,1).
+ enum {NUM_SUBDIVS = 8};
+ CUtlVector<int> m_NormalGrid[NUM_SUBDIVS][NUM_SUBDIVS][NUM_SUBDIVS];
+};
+
+
+int g_iCurFace;
+edgeshare_t edgeshare[MAX_MAP_EDGES];
+
+Vector face_centroids[MAX_MAP_EDGES];
+
+int vertexref[MAX_MAP_VERTS];
+int *vertexface[MAX_MAP_VERTS];
+faceneighbor_t faceneighbor[MAX_MAP_FACES];
+
+static directlight_t *gSkyLight = NULL;
+static directlight_t *gAmbient = NULL;
+
+//==========================================================================//
+// CNormalList implementation.
+//==========================================================================//
+
+CNormalList::CNormalList() : m_Normals( 128 )
+{
+ for( int i=0; i < sizeof(m_NormalGrid)/sizeof(m_NormalGrid[0][0][0]); i++ )
+ {
+ (&m_NormalGrid[0][0][0] + i)->SetGrowSize( 16 );
+ }
+}
+
+int CNormalList::FindOrAddNormal( Vector const &vNormal )
+{
+ int gi[3];
+
+ // See which grid element it's in.
+ for( int iDim=0; iDim < 3; iDim++ )
+ {
+ gi[iDim] = (int)( ((vNormal[iDim] + 1.0f) * 0.5f) * NUM_SUBDIVS - 0.000001f );
+ gi[iDim] = min( gi[iDim], NUM_SUBDIVS );
+ gi[iDim] = max( gi[iDim], 0 );
+ }
+
+ // Look for a matching vector in there.
+ CUtlVector<int> *pGridElement = &m_NormalGrid[gi[0]][gi[1]][gi[2]];
+ for( int i=0; i < pGridElement->Size(); i++ )
+ {
+ int iNormal = pGridElement->Element(i);
+
+ Vector *pVec = &m_Normals[iNormal];
+ //if( pVec->DistToSqr(vNormal) < 0.00001f )
+ if( *pVec == vNormal )
+ return iNormal;
+ }
+
+ // Ok, add a new one.
+ pGridElement->AddToTail( m_Normals.Size() );
+ return m_Normals.AddToTail( vNormal );
+}
+
+// FIXME: HACK until the plane normals are made more happy
+void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
+ const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] )
+{
+ Vector stmp( sVect[0], sVect[1], sVect[2] );
+ Vector ttmp( tVect[0], tVect[1], tVect[2] );
+ GetBumpNormals( stmp, ttmp, flatNormal, phongNormal, bumpNormals );
+}
+
+int EdgeVertex( dface_t *f, int edge )
+{
+ int k;
+
+ if (edge < 0)
+ edge += f->numedges;
+ else if (edge >= f->numedges)
+ edge = edge % f->numedges;
+
+ k = dsurfedges[f->firstedge + edge];
+ if (k < 0)
+ {
+ // Msg("(%d %d) ", dedges[-k].v[1], dedges[-k].v[0] );
+ return dedges[-k].v[1];
+ }
+ else
+ {
+ // Msg("(%d %d) ", dedges[k].v[0], dedges[k].v[1] );
+ return dedges[k].v[0];
+ }
+}
+
+
+/*
+ ============
+ PairEdges
+ ============
+*/
+void PairEdges (void)
+{
+ int i, j, k, n, m;
+ dface_t *f;
+ int numneighbors;
+ int tmpneighbor[64];
+ faceneighbor_t *fn;
+
+ // count number of faces that reference each vertex
+ for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
+ {
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ // Store the count in vertexref
+ vertexref[EdgeVertex(f,j)]++;
+ }
+ }
+
+ // allocate room
+ for (i = 0; i < numvertexes; i++)
+ {
+ // use the count from above to allocate a big enough array
+ vertexface[i] = ( int* )calloc( vertexref[i], sizeof( vertexface[0] ) );
+ // clear the temporary data
+ vertexref[i] = 0;
+ }
+
+ // store a list of every face that uses a particular vertex
+ for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
+ {
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ n = EdgeVertex(f,j);
+
+ for (k = 0; k < vertexref[n]; k++)
+ {
+ if (vertexface[n][k] == i)
+ break;
+ }
+ if (k >= vertexref[n])
+ {
+ // add the face to the list
+ vertexface[n][k] = i;
+ vertexref[n]++;
+ }
+ }
+ }
+
+ // calc normals and set displacement surface flag
+ for (i=0, f = g_pFaces; i<numfaces ; i++, f++)
+ {
+ fn = &faceneighbor[i];
+
+ // get face normal
+ VectorCopy( dplanes[f->planenum].normal, fn->facenormal );
+
+ // set displacement surface flag
+ fn->bHasDisp = false;
+ if( ValidDispFace( f ) )
+ {
+ fn->bHasDisp = true;
+ }
+ }
+
+ // find neighbors
+ for (i=0, f = g_pFaces ; i<numfaces ; i++, f++)
+ {
+ numneighbors = 0;
+ fn = &faceneighbor[i];
+
+ // allocate room for vertex normals
+ fn->normal = ( Vector* )calloc( f->numedges, sizeof( fn->normal[0] ) );
+
+ // look up all faces sharing vertices and add them to the list
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ n = EdgeVertex(f,j);
+
+ for (k = 0; k < vertexref[n]; k++)
+ {
+ double cos_normals_angle;
+ Vector *pNeighbornormal;
+
+ // skip self
+ if (vertexface[n][k] == i)
+ continue;
+
+ // if this face doens't have a displacement -- don't consider displacement neighbors
+ if( ( !fn->bHasDisp ) && ( faceneighbor[vertexface[n][k]].bHasDisp ) )
+ continue;
+
+ pNeighbornormal = &faceneighbor[vertexface[n][k]].facenormal;
+ cos_normals_angle = DotProduct( *pNeighbornormal, fn->facenormal );
+
+ // add normal if >= threshold or its a displacement surface (this is only if the original
+ // face is a displacement)
+ if ( fn->bHasDisp )
+ {
+ // Always smooth with and against a displacement surface.
+ VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
+ }
+ else
+ {
+ // No smoothing - use of method (backwards compatibility).
+ if ( ( f->smoothingGroups == 0 ) && ( g_pFaces[vertexface[n][k]].smoothingGroups == 0 ) )
+ {
+ if ( cos_normals_angle >= smoothing_threshold )
+ {
+ VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
+ }
+ else
+ {
+ // not considered a neighbor
+ continue;
+ }
+ }
+ else
+ {
+ unsigned int smoothingGroup = ( f->smoothingGroups & g_pFaces[vertexface[n][k]].smoothingGroups );
+
+ // Hard edge.
+ if ( ( smoothingGroup & SMOOTHING_GROUP_HARD_EDGE ) != 0 )
+ continue;
+
+ if ( smoothingGroup != 0 )
+ {
+ VectorAdd( fn->normal[j], *pNeighbornormal, fn->normal[j] );
+ }
+ else
+ {
+ // not considered a neighbor
+ continue;
+ }
+ }
+ }
+
+ // look to see if we've already added this one
+ for (m = 0; m < numneighbors; m++)
+ {
+ if (tmpneighbor[m] == vertexface[n][k])
+ break;
+ }
+
+ if (m >= numneighbors)
+ {
+ // add to neighbor list
+ tmpneighbor[m] = vertexface[n][k];
+ numneighbors++;
+ if ( numneighbors > ARRAYSIZE(tmpneighbor) )
+ {
+ Error("Stack overflow in neighbors\n");
+ }
+ }
+ }
+ }
+
+ if (numneighbors)
+ {
+ // copy over neighbor list
+ fn->numneighbors = numneighbors;
+ fn->neighbor = ( int* )calloc( numneighbors, sizeof( fn->neighbor[0] ) );
+ for (m = 0; m < numneighbors; m++)
+ {
+ fn->neighbor[m] = tmpneighbor[m];
+ }
+ }
+
+ // fixup normals
+ for (j = 0; j < f->numedges; j++)
+ {
+ VectorAdd( fn->normal[j], fn->facenormal, fn->normal[j] );
+ VectorNormalize( fn->normal[j] );
+ }
+ }
+}
+
+
+void SaveVertexNormals( void )
+{
+ faceneighbor_t *fn;
+ int i, j;
+ dface_t *f;
+ CNormalList normalList;
+
+ g_numvertnormalindices = 0;
+
+ for( i = 0 ;i<numfaces ; i++ )
+ {
+ fn = &faceneighbor[i];
+ f = &g_pFaces[i];
+
+ for( j = 0; j < f->numedges; j++ )
+ {
+ Vector vNormal;
+ if( fn->normal )
+ {
+ vNormal = fn->normal[j];
+ }
+ else
+ {
+ // original faces don't have normals
+ vNormal.Init( 0, 0, 0 );
+ }
+
+ if( g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES )
+ {
+ Error( "g_numvertnormalindices == MAX_MAP_VERTNORMALINDICES" );
+ }
+
+ g_vertnormalindices[g_numvertnormalindices] = (unsigned short)normalList.FindOrAddNormal( vNormal );
+ g_numvertnormalindices++;
+ }
+ }
+
+ if( normalList.m_Normals.Size() > MAX_MAP_VERTNORMALS )
+ {
+ Error( "g_numvertnormals > MAX_MAP_VERTNORMALS" );
+ }
+
+ // Copy the list of unique vert normals into g_vertnormals.
+ g_numvertnormals = normalList.m_Normals.Size();
+ memcpy( g_vertnormals, normalList.m_Normals.Base(), sizeof(g_vertnormals[0]) * normalList.m_Normals.Size() );
+}
+
+/*
+ =================================================================
+
+ LIGHTMAP SAMPLE GENERATION
+
+ =================================================================
+*/
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Spits out an error message with information about a lightinfo_t.
+// Input : s - Error message string.
+// l - lightmap info struct.
+//-----------------------------------------------------------------------------
+void ErrorLightInfo(const char *s, lightinfo_t *l)
+{
+ texinfo_t *tex = &texinfo[l->face->texinfo];
+ winding_t *w = WindingFromFace(&g_pFaces[l->facenum], l->modelorg);
+
+ //
+ // Show the face center and material name if possible.
+ //
+ if (w != NULL)
+ {
+ // Don't exit, we'll try to recover...
+ Vector vecCenter;
+ WindingCenter(w, vecCenter);
+// FreeWinding(w);
+
+ Warning("%s at (%g, %g, %g)\n\tmaterial=%s\n", s, (double)vecCenter.x, (double)vecCenter.y, (double)vecCenter.z, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ) );
+ }
+ //
+ // If not, just show the material name.
+ //
+ else
+ {
+ Warning("%s at (degenerate face)\n\tmaterial=%s\n", s, TexDataStringTable_GetString( dtexdata[tex->texdata].nameStringTableID ));
+ }
+}
+
+
+
+void CalcFaceVectors(lightinfo_t *l)
+{
+ texinfo_t *tex;
+ int i, j;
+
+ tex = &texinfo[l->face->texinfo];
+
+ // move into lightinfo_t
+ for (i=0 ; i<2 ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ {
+ l->worldToLuxelSpace[i][j] = tex->lightmapVecsLuxelsPerWorldUnits[i][j];
+ }
+ }
+
+ //Solve[ { x * w00 + y * w01 + z * w02 - s == 0, x * w10 + y * w11 + z * w12 - t == 0, A * x + B * y + C * z + D == 0 }, { x, y, z } ]
+ //Rule(x,( C*s*w11 - B*s*w12 + B*t*w02 - C*t*w01 + D*w02*w11 - D*w01*w12) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
+ //Rule(y,( A*s*w12 - C*s*w10 + C*t*w00 - A*t*w02 + D*w00*w12 - D*w02*w10) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 )),
+ //Rule(z,( B*s*w10 - A*s*w11 + A*t*w01 - B*t*w00 + D*w01*w10 - D*w00*w11) / (+ A*w01*w12 - A*w02*w11 + B*w02*w10 - B*w00*w12 + C*w00*w11 - C*w01*w10 ))))
+
+ Vector luxelSpaceCross;
+
+ luxelSpaceCross[0] =
+ tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][2] -
+ tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][1];
+ luxelSpaceCross[1] =
+ tex->lightmapVecsLuxelsPerWorldUnits[1][2] * tex->lightmapVecsLuxelsPerWorldUnits[0][0] -
+ tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][2];
+ luxelSpaceCross[2] =
+ tex->lightmapVecsLuxelsPerWorldUnits[1][0] * tex->lightmapVecsLuxelsPerWorldUnits[0][1] -
+ tex->lightmapVecsLuxelsPerWorldUnits[1][1] * tex->lightmapVecsLuxelsPerWorldUnits[0][0];
+
+ float det = -DotProduct( l->facenormal, luxelSpaceCross );
+ if ( fabs( det ) < 1.0e-20 )
+ {
+ Warning(" warning - face vectors parallel to face normal. bad lighting will be produced\n" );
+ l->luxelOrigin = vec3_origin;
+ }
+ else
+ {
+ // invert the matrix
+ l->luxelToWorldSpace[0][0] = (l->facenormal[2] * l->worldToLuxelSpace[1][1] - l->facenormal[1] * l->worldToLuxelSpace[1][2]) / det;
+ l->luxelToWorldSpace[1][0] = (l->facenormal[1] * l->worldToLuxelSpace[0][2] - l->facenormal[2] * l->worldToLuxelSpace[0][1]) / det;
+ l->luxelOrigin[0] = -(l->facedist * luxelSpaceCross[0]) / det;
+ l->luxelToWorldSpace[0][1] = (l->facenormal[0] * l->worldToLuxelSpace[1][2] - l->facenormal[2] * l->worldToLuxelSpace[1][0]) / det;
+ l->luxelToWorldSpace[1][1] = (l->facenormal[2] * l->worldToLuxelSpace[0][0] - l->facenormal[0] * l->worldToLuxelSpace[0][2]) / det;
+ l->luxelOrigin[1] = -(l->facedist * luxelSpaceCross[1]) / det;
+ l->luxelToWorldSpace[0][2] = (l->facenormal[1] * l->worldToLuxelSpace[1][0] - l->facenormal[0] * l->worldToLuxelSpace[1][1]) / det;
+ l->luxelToWorldSpace[1][2] = (l->facenormal[0] * l->worldToLuxelSpace[0][1] - l->facenormal[1] * l->worldToLuxelSpace[0][0]) / det;
+ l->luxelOrigin[2] = -(l->facedist * luxelSpaceCross[2]) / det;
+
+ // adjust for luxel offset
+ VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[0][3], l->luxelToWorldSpace[0], l->luxelOrigin );
+ VectorMA( l->luxelOrigin, -tex->lightmapVecsLuxelsPerWorldUnits[1][3], l->luxelToWorldSpace[1], l->luxelOrigin );
+ }
+ // compensate for org'd bmodels
+ VectorAdd (l->luxelOrigin, l->modelorg, l->luxelOrigin);
+}
+
+
+
+winding_t *LightmapCoordWindingForFace( lightinfo_t *l )
+{
+ int i;
+ winding_t *w;
+
+ w = WindingFromFace( l->face, l->modelorg );
+
+ for (i = 0; i < w->numpoints; i++)
+ {
+ Vector2D coord;
+ WorldToLuxelSpace( l, w->p[i], coord );
+ w->p[i].x = coord.x;
+ w->p[i].y = coord.y;
+ w->p[i].z = 0;
+ }
+
+ return w;
+}
+
+
+void WriteCoordWinding (FILE *out, lightinfo_t *l, winding_t *w, Vector& color )
+{
+ int i;
+ Vector pos;
+
+ fprintf (out, "%i\n", w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ LuxelSpaceToWorld( l, w->p[i][0], w->p[i][1], pos );
+ fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ pos[0],
+ pos[1],
+ pos[2],
+ color[ 0 ] / 256,
+ color[ 1 ] / 256,
+ color[ 2 ] / 256 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void DumpFaces( lightinfo_t *pLightInfo, int ndxFace )
+{
+ static FileHandle_t out;
+
+ // get face data
+ faceneighbor_t *fn = &faceneighbor[ndxFace];
+ Vector ¢roid = face_centroids[ndxFace];
+
+ // disable threading (not a multi-threadable function!)
+ ThreadLock();
+
+ if( !out )
+ {
+ // open the file
+ out = g_pFileSystem->Open( "face.txt", "w" );
+ if( !out )
+ return;
+ }
+
+ //
+ // write out face
+ //
+ for( int ndxEdge = 0; ndxEdge < pLightInfo->face->numedges; ndxEdge++ )
+ {
+// int edge = dsurfedges[pLightInfo->face->firstedge+ndxEdge];
+
+ Vector p1, p2;
+ VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge )].point, pLightInfo->modelorg, p1 );
+ VectorAdd( dvertexes[EdgeVertex( pLightInfo->face, ndxEdge+1 )].point, pLightInfo->modelorg, p2 );
+
+ Vector &n1 = fn->normal[ndxEdge];
+ Vector &n2 = fn->normal[(ndxEdge+1)%pLightInfo->face->numedges];
+
+ CmdLib_FPrintf( out, "3\n");
+
+ CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p1[0], p1[1], p1[2], n1[0] * 0.5 + 0.5, n1[1] * 0.5 + 0.5, n1[2] * 0.5 + 0.5 );
+
+ CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", p2[0], p2[1], p2[2], n2[0] * 0.5 + 0.5, n2[1] * 0.5 + 0.5, n2[2] * 0.5 + 0.5 );
+
+ CmdLib_FPrintf(out, "%f %f %f %f %f %f\n", centroid[0] + pLightInfo->modelorg[0],
+ centroid[1] + pLightInfo->modelorg[1],
+ centroid[2] + pLightInfo->modelorg[2],
+ fn->facenormal[0] * 0.5 + 0.5,
+ fn->facenormal[1] * 0.5 + 0.5,
+ fn->facenormal[2] * 0.5 + 0.5 );
+
+ }
+
+ // enable threading
+ ThreadUnlock();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildFacesamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
+{
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // ratio of world area / lightmap area
+ texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
+ pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
+ pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
+ sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
+ pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
+
+ //
+ // quickly create samples and luxels (copy over samples)
+ //
+ pFaceLight->numsamples = width * height;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ return false;
+
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ if( !pFaceLight->luxel )
+ return false;
+
+ sample_t *pSamples = pFaceLight->sample;
+ Vector *pLuxels = pFaceLight->luxel;
+
+ for( int t = 0; t < height; t++ )
+ {
+ for( int s = 0; s < width; s++ )
+ {
+ pSamples->s = s;
+ pSamples->t = t;
+ pSamples->coord[0] = s;
+ pSamples->coord[1] = t;
+ // unused but initialized anyway
+ pSamples->mins[0] = s - 0.5;
+ pSamples->mins[1] = t - 0.5;
+ pSamples->maxs[0] = s + 0.5;
+ pSamples->maxs[1] = t + 0.5;
+ pSamples->area = pFaceLight->worldAreaPerLuxel;
+ LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
+ VectorCopy( pSamples->pos, *pLuxels );
+
+ pSamples++;
+ pLuxels++;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // build samples for a "face"
+ if( pLightInfo->face->dispinfo == -1 )
+ {
+ return BuildFacesamplesAndLuxels_DoFast( pLightInfo, pFaceLight );
+ }
+ // build samples for a "displacement"
+ else
+ {
+ return StaticDispMgr()->BuildDispSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildFacesamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
+{
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // ratio of world area / lightmap area
+ texinfo_t *pTex = &texinfo[pLightInfo->face->texinfo];
+ pFaceLight->worldAreaPerLuxel = 1.0 / ( sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[0],
+ pTex->lightmapVecsLuxelsPerWorldUnits[0] ) ) *
+ sqrt( DotProduct( pTex->lightmapVecsLuxelsPerWorldUnits[1],
+ pTex->lightmapVecsLuxelsPerWorldUnits[1] ) ) );
+
+ // allocate a large number of samples for creation -- get copied later!
+ char sampleData[sizeof(sample_t)*SINGLE_BRUSH_MAP*2];
+ sample_t *samples = (sample_t*)sampleData; // use a char array to speed up the debug version.
+ sample_t *pSamples = samples;
+
+ // lightmap space winding
+ winding_t *pLightmapWinding = LightmapCoordWindingForFace( pLightInfo );
+
+ //
+ // build vector pointing along the lightmap cutting planes
+ //
+ Vector sNorm( 1.0f, 0.0f, 0.0f );
+ Vector tNorm( 0.0f, 1.0f, 0.0f );
+
+ // sample center offset
+ float sampleOffset = ( do_centersamples ) ? 0.5 : 1.0;
+
+ //
+ // clip the lightmap "spaced" winding by the lightmap cutting planes
+ //
+ winding_t *pWindingT1, *pWindingT2;
+ winding_t *pWindingS1, *pWindingS2;
+ float dist;
+
+ for( int t = 0; t < height && pLightmapWinding; t++ )
+ {
+ dist = t + sampleOffset;
+
+ // lop off a sample in the t dimension
+ // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
+ ClipWindingEpsilon( pLightmapWinding, tNorm, dist, ON_EPSILON / 16.0f, &pWindingT1, &pWindingT2 );
+
+ for( int s = 0; s < width && pWindingT2; s++ )
+ {
+ dist = s + sampleOffset;
+
+ // lop off a sample in the s dimension, and put it in ws2
+ // hack - need a separate epsilon for lightmap space since ON_EPSILON is for texture space
+ ClipWindingEpsilon( pWindingT2, sNorm, dist, ON_EPSILON / 16.0f, &pWindingS1, &pWindingS2 );
+
+ //
+ // s2 winding is a single sample worth of winding
+ //
+ if( pWindingS2 )
+ {
+ // save the s, t positions
+ pSamples->s = s;
+ pSamples->t = t;
+
+ // get the lightmap space area of ws2 and convert to world area
+ // and find the center (then convert it to 2D)
+ Vector center;
+ pSamples->area = WindingAreaAndBalancePoint( pWindingS2, center ) * pFaceLight->worldAreaPerLuxel;
+ pSamples->coord[0] = center.x;
+ pSamples->coord[1] = center.y;
+
+ // find winding bounds (then convert it to 2D)
+ Vector minbounds, maxbounds;
+ WindingBounds( pWindingS2, minbounds, maxbounds );
+ pSamples->mins[0] = minbounds.x;
+ pSamples->mins[1] = minbounds.y;
+ pSamples->maxs[0] = maxbounds.x;
+ pSamples->maxs[1] = maxbounds.y;
+
+ // convert from lightmap space to world space
+ LuxelSpaceToWorld( pLightInfo, pSamples->coord[0], pSamples->coord[1], pSamples->pos );
+
+ if (g_bDumpPatches || (do_extra && pSamples->area < pFaceLight->worldAreaPerLuxel - EQUAL_EPSILON))
+ {
+ //
+ // convert the winding from lightmaps space to world for debug rendering and sub-sampling
+ //
+ Vector worldPos;
+ for( int ndxPt = 0; ndxPt < pWindingS2->numpoints; ndxPt++ )
+ {
+ LuxelSpaceToWorld( pLightInfo, pWindingS2->p[ndxPt].x, pWindingS2->p[ndxPt].y, worldPos );
+ VectorCopy( worldPos, pWindingS2->p[ndxPt] );
+ }
+ pSamples->w = pWindingS2;
+ }
+ else
+ {
+ // winding isn't needed, free it.
+ pSamples->w = NULL;
+ FreeWinding( pWindingS2 );
+ }
+
+ pSamples++;
+ }
+
+ //
+ // if winding T2 still exists free it and set it equal S1 (the rest of the row minus the sample just created)
+ //
+ if( pWindingT2 )
+ {
+ FreeWinding( pWindingT2 );
+ }
+
+ // clip the rest of "s"
+ pWindingT2 = pWindingS1;
+ }
+
+ //
+ // if the original lightmap winding exists free it and set it equal to T1 (the rest of the winding not cut into samples)
+ //
+ if( pLightmapWinding )
+ {
+ FreeWinding( pLightmapWinding );
+ }
+
+ if( pWindingT2 )
+ {
+ FreeWinding( pWindingT2 );
+ }
+
+ pLightmapWinding = pWindingT1;
+ }
+
+ //
+ // copy over samples
+ //
+ pFaceLight->numsamples = pSamples - samples;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ return false;
+
+ memcpy( pFaceLight->sample, samples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
+
+ // supply a default sample normal (face normal - assumed flat)
+ for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
+ {
+ Assert ( VectorLength ( pLightInfo->facenormal ) > 1.0e-20);
+ pFaceLight->sample[ndxSample].normal = pLightInfo->facenormal;
+ }
+
+ // statistics - warning?!
+ if( pFaceLight->numsamples == 0 )
+ {
+ Msg( "no samples %d\n", pLightInfo->face - g_pFaces );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Free any windings used by this facelight. It's currently assumed they're not needed again
+//-----------------------------------------------------------------------------
+void FreeSampleWindings( facelight_t *fl )
+{
+ int i;
+ for (i = 0; i < fl->numsamples; i++)
+ {
+ if (fl->sample[i].w)
+ {
+ FreeWinding( fl->sample[i].w );
+ fl->sample[i].w = NULL;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: build the sample data for each lightmapped primitive type
+//-----------------------------------------------------------------------------
+bool BuildSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // build samples for a "face"
+ if( pLightInfo->face->dispinfo == -1 )
+ {
+ return BuildFacesamples( pLightInfo, pFaceLight );
+ }
+ // build samples for a "displacement"
+ else
+ {
+ return StaticDispMgr()->BuildDispSamples( pLightInfo, pFaceLight, ndxFace );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool BuildFaceLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight )
+{
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calcuate actual luxel points
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ if( !pFaceLight->luxel )
+ return false;
+
+ for( int t = 0; t < height; t++ )
+ {
+ for( int s = 0; s < width; s++ )
+ {
+ LuxelSpaceToWorld( pLightInfo, s, t, pFaceLight->luxel[s+t*width] );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: build the luxels (find the luxel centers) for each lightmapped
+// primitive type
+//-----------------------------------------------------------------------------
+bool BuildLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // build luxels for a "face"
+ if( pLightInfo->face->dispinfo == -1 )
+ {
+ return BuildFaceLuxels( pLightInfo, pFaceLight );
+ }
+ // build luxels for a "displacement"
+ else
+ {
+ return StaticDispMgr()->BuildDispLuxels( pLightInfo, pFaceLight, ndxFace );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: for each face, find the center of each luxel; for each texture
+// aligned grid point, back project onto the plane and get the world
+// xyz value of the sample point
+// NOTE: ndxFace = facenum
+//-----------------------------------------------------------------------------
+void CalcPoints( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // debugging!
+ if( g_bDumpPatches )
+ {
+ DumpFaces( pLightInfo, ndxFace );
+ }
+
+ // quick and dirty!
+ if( do_fast )
+ {
+ if( !BuildSamplesAndLuxels_DoFast( pLightInfo, pFaceLight, ndxFace ) )
+ {
+ Msg( "Face %d: (Fast)Error Building Samples and Luxels\n", ndxFace );
+ }
+ return;
+ }
+
+ // build the samples
+ if( !BuildSamples( pLightInfo, pFaceLight, ndxFace ) )
+ {
+ Msg( "Face %d: Error Building Samples\n", ndxFace );
+ }
+
+ // build the luxels
+ if( !BuildLuxels( pLightInfo, pFaceLight, ndxFace ) )
+ {
+ Msg( "Face %d: Error Building Luxels\n", ndxFace );
+ }
+}
+
+
+//==============================================================
+
+directlight_t *activelights;
+directlight_t *freelights;
+
+facelight_t facelight[MAX_MAP_FACES];
+int numdlights;
+
+/*
+ ==================
+ FindTargetEntity
+ ==================
+*/
+entity_t *FindTargetEntity (char *target)
+{
+ int i;
+ char *n;
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ n = ValueForKey (&entities[i], "targetname");
+ if (!strcmp (n, target))
+ return &entities[i];
+ }
+
+ return NULL;
+}
+
+
+
+/*
+ =============
+ AllocDLight
+ =============
+*/
+
+int GetVisCache( int lastoffset, int cluster, byte *pvs );
+void SetDLightVis( directlight_t *dl, int cluster );
+void MergeDLightVis( directlight_t *dl, int cluster );
+
+directlight_t *AllocDLight( Vector& origin, bool bAddToList )
+{
+ directlight_t *dl;
+
+ dl = ( directlight_t* )calloc(1, sizeof(directlight_t));
+ dl->index = numdlights++;
+
+ VectorCopy( origin, dl->light.origin );
+
+ dl->light.cluster = ClusterFromPoint(dl->light.origin);
+ SetDLightVis( dl, dl->light.cluster );
+
+ dl->facenum = -1;
+
+ if ( bAddToList )
+ {
+ dl->next = activelights;
+ activelights = dl;
+ }
+
+ return dl;
+}
+
+void AddDLightToActiveList( directlight_t *dl )
+{
+ dl->next = activelights;
+ activelights = dl;
+}
+
+void FreeDLights()
+{
+ gSkyLight = NULL;
+ gAmbient = NULL;
+
+ directlight_t *pNext;
+ for( directlight_t *pCur=activelights; pCur; pCur=pNext )
+ {
+ pNext = pCur->next;
+ free( pCur );
+ }
+ activelights = 0;
+}
+
+
+void SetDLightVis( directlight_t *dl, int cluster )
+{
+ if (dl->pvs == NULL)
+ {
+ dl->pvs = (byte *)calloc( 1, (dvis->numclusters / 8) + 1 );
+ }
+
+ GetVisCache( -1, cluster, dl->pvs );
+}
+
+void MergeDLightVis( directlight_t *dl, int cluster )
+{
+ if (dl->pvs == NULL)
+ {
+ SetDLightVis( dl, cluster );
+ }
+ else
+ {
+ byte pvs[MAX_MAP_CLUSTERS/8];
+ GetVisCache( -1, cluster, pvs );
+
+ // merge both vis graphs
+ for (int i = 0; i < (dvis->numclusters / 8) + 1; i++)
+ {
+ dl->pvs[i] |= pvs[i];
+ }
+ }
+}
+
+
+/*
+ =============
+ LightForKey
+ =============
+*/
+int LightForKey (entity_t *ent, char *key, Vector& intensity )
+{
+ char *pLight;
+
+ pLight = ValueForKey( ent, key );
+
+ return LightForString( pLight, intensity );
+}
+
+int LightForString( char *pLight, Vector& intensity )
+{
+ double r, g, b, scaler;
+ int argCnt;
+
+ VectorFill( intensity, 0 );
+
+ // scanf into doubles, then assign, so it is vec_t size independent
+ r = g = b = scaler = 0;
+ double r_hdr,g_hdr,b_hdr,scaler_hdr;
+ argCnt = sscanf ( pLight, "%lf %lf %lf %lf %lf %lf %lf %lf",
+ &r, &g, &b, &scaler, &r_hdr,&g_hdr,&b_hdr,&scaler_hdr );
+
+ if (argCnt==8) // 2 4-tuples
+ {
+ if (g_bHDR)
+ {
+ r=r_hdr;
+ g=g_hdr;
+ b=b_hdr;
+ scaler=scaler_hdr;
+ }
+ argCnt=4;
+ }
+
+ // make sure light is legal
+ if( r < 0.0f || g < 0.0f || b < 0.0f || scaler < 0.0f )
+ {
+ intensity.Init( 0.0f, 0.0f, 0.0f );
+ return false;
+ }
+
+ intensity[0] = pow( r / 255.0, 2.2 ) * 255; // convert to linear
+
+ switch( argCnt)
+ {
+ case 1:
+ // The R,G,B values are all equal.
+ intensity[1] = intensity[2] = intensity[0];
+ break;
+
+ case 3:
+ case 4:
+ // Save the other two G,B values.
+ intensity[1] = pow( g / 255.0, 2.2 ) * 255;
+ intensity[2] = pow( b / 255.0, 2.2 ) * 255;
+
+ // Did we also get an "intensity" scaler value too?
+ if ( argCnt == 4 )
+ {
+ // Scale the normalized 0-255 R,G,B values by the intensity scaler
+ VectorScale( intensity, scaler / 255.0, intensity );
+ }
+ break;
+
+ default:
+ printf("unknown light specifier type - %s\n",pLight);
+ return false;
+ }
+ // scale up source lights by scaling factor
+ VectorScale( intensity, lightscale, intensity );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Various parsing methods
+//-----------------------------------------------------------------------------
+
+static void ParseLightGeneric( entity_t *e, directlight_t *dl )
+{
+ entity_t *e2;
+ char *target;
+ Vector dest;
+
+ dl->light.style = (int)FloatForKey (e, "style");
+
+ // get intenfsity
+ if( g_bHDR && LightForKey( e, "_lightHDR", dl->light.intensity ) )
+ {
+ }
+ else
+ {
+ LightForKey( e, "_light", dl->light.intensity );
+ }
+
+ // check angle, targets
+ target = ValueForKey (e, "target");
+ if (target[0])
+ { // point towards target
+ e2 = FindTargetEntity (target);
+ if (!e2)
+ Warning("WARNING: light at (%i %i %i) has missing target\n",
+ (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
+ else
+ {
+ GetVectorForKey (e2, "origin", dest);
+ VectorSubtract (dest, dl->light.origin, dl->light.normal);
+ VectorNormalize (dl->light.normal);
+ }
+ }
+ else
+ {
+ // point down angle
+ Vector angles;
+ GetVectorForKey( e, "angles", angles );
+ float pitch = FloatForKey (e, "pitch");
+ float angle = FloatForKey (e, "angle");
+ SetupLightNormalFromProps( QAngle( angles.x, angles.y, angles.z ), angle, pitch, dl->light.normal );
+ }
+ if ( g_bHDR )
+ VectorScale( dl->light.intensity,
+ FloatForKeyWithDefault( e, "_lightscaleHDR", 1.0 ),
+ dl->light.intensity );
+}
+
+static void SetLightFalloffParams( entity_t * e, directlight_t * dl )
+{
+ float d50=FloatForKey( e, "_fifty_percent_distance" );
+ dl->m_flStartFadeDistance = 0;
+ dl->m_flEndFadeDistance = - 1;
+ dl->m_flCapDist = 1.0e22;
+ if ( d50 )
+ {
+ float d0 = FloatForKey( e, "_zero_percent_distance" );
+ if ( d0 < d50 )
+ {
+ Warning( "light has _fifty_percent_distance of %f but _zero_percent_distance of %f\n", d50, d0);
+ d0 = 2.0 * d50;
+ }
+ float a = 0, b = 1, c = 0;
+ if ( ! SolveInverseQuadraticMonotonic( 0, 1.0, d50, 2.0, d0, 256.0, a, b, c ))
+ {
+ Warning( "can't solve quadratic for light %f %f\n", d50, d0 );
+ }
+ // it it possible that the parameters couldn't be used because of enforing monoticity. If so, rescale so at
+ // least the 50 percent value is right
+// printf("50 percent=%f 0 percent=%f\n",d50,d0);
+// printf("a=%f b=%f c=%f\n",a,b,c);
+ float v50 = c + d50 * ( b + d50 * a );
+ float scale = 2.0 / v50;
+ a *= scale;
+ b *= scale;
+ c *= scale;
+// printf("scaled=%f a=%f b=%f c=%f\n",scale,a,b,c);
+// for(float d=0;d<1000;d+=20)
+// printf("at %f, %f\n",d,1.0/(c+d*(b+d*a)));
+ dl->light.quadratic_attn = a;
+ dl->light.linear_attn = b;
+ dl->light.constant_attn = c;
+
+
+
+ if ( IntForKey(e, "_hardfalloff" ) )
+ {
+ dl->m_flEndFadeDistance = d0;
+ dl->m_flStartFadeDistance = 0.75 * d0 + 0.25 * d50; // start fading 3/4 way between 50 and 0. could allow adjust
+ }
+ else
+ {
+ // now, we will find the point at which the 1/x term reaches its maximum value, and
+ // prevent the light from going past there. If a user specifes an extreme falloff, the
+ // quadratic will start making the light brighter at some distance. We handle this by
+ // fading it from the minimum brightess point down to zero at 10x the minimum distance
+ if ( fabs( a ) > 0. )
+ {
+ float flMax = b / ( - 2.0 * a ); // where f' = 0
+ if ( flMax > 0.0 )
+ {
+ dl->m_flCapDist = flMax;
+ dl->m_flStartFadeDistance = flMax;
+ dl->m_flEndFadeDistance = 10.0 * flMax;
+ }
+ }
+ }
+ }
+ else
+ {
+ dl->light.constant_attn = FloatForKey (e, "_constant_attn" );
+ dl->light.linear_attn = FloatForKey (e, "_linear_attn" );
+ dl->light.quadratic_attn = FloatForKey (e, "_quadratic_attn" );
+
+ dl->light.radius = FloatForKey (e, "_distance");
+
+ // clamp values to >= 0
+ if ( dl->light.constant_attn < EQUAL_EPSILON )
+ dl->light.constant_attn = 0;
+
+ if ( dl->light.linear_attn < EQUAL_EPSILON )
+ dl->light.linear_attn = 0;
+
+ if ( dl->light.quadratic_attn < EQUAL_EPSILON )
+ dl->light.quadratic_attn = 0;
+
+ if ( dl->light.constant_attn < EQUAL_EPSILON && dl->light.linear_attn < EQUAL_EPSILON && dl->light.quadratic_attn < EQUAL_EPSILON )
+ dl->light.constant_attn = 1;
+
+ // scale intensity for unit 100 distance
+ float ratio = ( dl->light.constant_attn + 100 * dl->light.linear_attn + 100 * 100 * dl->light.quadratic_attn );
+ if ( ratio > 0 )
+ {
+ VectorScale( dl->light.intensity, ratio, dl->light.intensity );
+ }
+ }
+}
+
+static void ParseLightSpot( entity_t* e, directlight_t* dl )
+{
+ Vector dest;
+ GetVectorForKey (e, "origin", dest );
+ dl = AllocDLight( dest, true );
+
+ ParseLightGeneric( e, dl );
+
+ dl->light.type = emit_spotlight;
+
+ dl->light.stopdot = FloatForKey (e, "_inner_cone");
+ if (!dl->light.stopdot)
+ dl->light.stopdot = 10;
+
+ dl->light.stopdot2 = FloatForKey (e, "_cone");
+ if (!dl->light.stopdot2)
+ dl->light.stopdot2 = dl->light.stopdot;
+ if (dl->light.stopdot2 < dl->light.stopdot)
+ dl->light.stopdot2 = dl->light.stopdot;
+
+ // This is a point light if stop dots are 180...
+ if ((dl->light.stopdot == 180) && (dl->light.stopdot2 == 180))
+ {
+ dl->light.stopdot = dl->light.stopdot2 = 0;
+ dl->light.type = emit_point;
+ dl->light.exponent = 0;
+ }
+ else
+ {
+ // Clamp to 90, that's all DX8 can handle!
+ if (dl->light.stopdot > 90)
+ {
+ Warning("WARNING: light_spot at (%i %i %i) has inner angle larger than 90 degrees! Clamping to 90...\n",
+ (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
+ dl->light.stopdot = 90;
+ }
+
+ if (dl->light.stopdot2 > 90)
+ {
+ Warning("WARNING: light_spot at (%i %i %i) has outer angle larger than 90 degrees! Clamping to 90...\n",
+ (int)dl->light.origin[0], (int)dl->light.origin[1], (int)dl->light.origin[2]);
+ dl->light.stopdot2 = 90;
+ }
+
+ dl->light.stopdot2 = (float)cos(dl->light.stopdot2/180*M_PI);
+ dl->light.stopdot = (float)cos(dl->light.stopdot/180*M_PI);
+ dl->light.exponent = FloatForKey (e, "_exponent");
+ }
+
+ SetLightFalloffParams(e,dl);
+}
+
+// NOTE: This is just a heuristic. It traces a finite number of rays to find sky
+// NOTE: Full vis is necessary to make this 100% correct.
+bool CanLeafTraceToSky( int iLeaf )
+{
+ // UNDONE: Really want a point inside the leaf here. Center is a guess, may not be in the leaf
+ // UNDONE: Clip this to each plane bounding the leaf to guarantee
+ Vector center = vec3_origin;
+ for ( int i = 0; i < 3; i++ )
+ {
+ center[i] = ( (float)(dleafs[iLeaf].mins[i] + dleafs[iLeaf].maxs[i]) ) * 0.5f;
+ }
+
+ FourVectors center4, delta;
+ fltx4 fractionVisible;
+ for ( int j = 0; j < NUMVERTEXNORMALS; j+=4 )
+ {
+ // search back to see if we can hit a sky brush
+ delta.LoadAndSwizzle( g_anorms[j], g_anorms[min( j+1, NUMVERTEXNORMALS-1 )],
+ g_anorms[min( j+2, NUMVERTEXNORMALS-1 )], g_anorms[min( j+3, NUMVERTEXNORMALS-1 )] );
+ delta *= -MAX_TRACE_LENGTH;
+ delta += center4;
+
+ // return true if any hits sky
+ TestLine_DoesHitSky ( center4, delta, &fractionVisible );
+ if ( TestSignSIMD ( CmpGtSIMD ( fractionVisible, Four_Zeros ) ) )
+ return true;
+ }
+
+ return false;
+}
+
+void BuildVisForLightEnvironment( void )
+{
+ // Create the vis.
+ for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
+ {
+ dleafs[iLeaf].flags &= ~( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D );
+ unsigned int iFirstFace = dleafs[iLeaf].firstleafface;
+ for ( int iLeafFace = 0; iLeafFace < dleafs[iLeaf].numleaffaces; ++iLeafFace )
+ {
+ unsigned int iFace = dleaffaces[iFirstFace+iLeafFace];
+
+ texinfo_t &tex = texinfo[g_pFaces[iFace].texinfo];
+ if ( tex.flags & SURF_SKY )
+ {
+ if ( tex.flags & SURF_SKY2D )
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
+ }
+ else
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
+ }
+ MergeDLightVis( gSkyLight, dleafs[iLeaf].cluster );
+ MergeDLightVis( gAmbient, dleafs[iLeaf].cluster );
+ break;
+ }
+ }
+ }
+
+ // Second pass to set flags on leaves that don't contain sky, but touch leaves that
+ // contain sky.
+ byte pvs[MAX_MAP_CLUSTERS / 8];
+
+ int nLeafBytes = (numleafs >> 3) + 1;
+ unsigned char *pLeafBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
+ unsigned char *pLeaf2DBits = (unsigned char *)stackalloc( nLeafBytes * sizeof(unsigned char) );
+ memset( pLeafBits, 0, nLeafBytes );
+ memset( pLeaf2DBits, 0, nLeafBytes );
+
+ for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
+ {
+ // If this leaf has light (3d skybox) in it, then don't bother
+ if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
+ continue;
+
+ // Don't bother with this leaf if it's solid
+ if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
+ continue;
+
+ // See what other leaves are visible from this leaf
+ GetVisCache( -1, dleafs[iLeaf].cluster, pvs );
+
+ // Now check out all other leaves
+ int nByte = iLeaf >> 3;
+ int nBit = 1 << ( iLeaf & 0x7 );
+ for ( int iLeaf2 = 0; iLeaf2 < numleafs; ++iLeaf2 )
+ {
+ if ( iLeaf2 == iLeaf )
+ continue;
+
+ if ( !(dleafs[iLeaf2].flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) ) )
+ continue;
+
+ // Can this leaf see into the leaf with the sky in it?
+ if ( !PVSCheck( pvs, dleafs[iLeaf2].cluster ) )
+ continue;
+
+ if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY2D )
+ {
+ pLeaf2DBits[ nByte ] |= nBit;
+ }
+ if ( dleafs[iLeaf2].flags & LEAF_FLAGS_SKY )
+ {
+ pLeafBits[ nByte ] |= nBit;
+
+ // As soon as we know this leaf needs to draw the 3d skybox, we're done
+ break;
+ }
+ }
+ }
+
+ // Must set the bits in a separate pass so as to not flood-fill LEAF_FLAGS_SKY everywhere
+ // pLeafbits is a bit array of all leaves that need to be marked as seeing sky
+ for ( int iLeaf = 0; iLeaf < numleafs; ++iLeaf )
+ {
+ // If this leaf has light (3d skybox) in it, then don't bother
+ if ( dleafs[iLeaf].flags & LEAF_FLAGS_SKY )
+ continue;
+
+ // Don't bother with this leaf if it's solid
+ if ( dleafs[iLeaf].contents & CONTENTS_SOLID )
+ continue;
+
+ // Check to see if this is a 2D skybox leaf
+ if ( pLeaf2DBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY2D;
+ }
+
+ // If this is a 3D skybox leaf, then we don't care if it was previously a 2D skybox leaf
+ if ( pLeafBits[ iLeaf >> 3 ] & (1 << ( iLeaf & 0x7 )) )
+ {
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
+ dleafs[iLeaf].flags &= ~LEAF_FLAGS_SKY2D;
+ }
+ else
+ {
+ // if radial vis was used on this leaf some of the portals leading
+ // to sky may have been culled. Try tracing to find sky.
+ if ( dleafs[iLeaf].flags & LEAF_FLAGS_RADIAL )
+ {
+ if ( CanLeafTraceToSky(iLeaf) )
+ {
+ // FIXME: Should make a version that checks if we hit 2D skyboxes.. oh well.
+ dleafs[iLeaf].flags |= LEAF_FLAGS_SKY;
+ }
+ }
+ }
+ }
+}
+
+static char *ValueForKeyWithDefault (entity_t *ent, char *key, char *default_value = NULL)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return ep->value;
+ return default_value;
+}
+
+static void ParseLightEnvironment( entity_t* e, directlight_t* dl )
+{
+ Vector dest;
+ GetVectorForKey (e, "origin", dest );
+ dl = AllocDLight( dest, false );
+
+ ParseLightGeneric( e, dl );
+
+ char *angle_str=ValueForKeyWithDefault( e, "SunSpreadAngle" );
+ if (angle_str)
+ {
+ g_SunAngularExtent=atof(angle_str);
+ g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
+ printf("sun extent from map=%f\n",g_SunAngularExtent);
+ }
+ if ( !gSkyLight )
+ {
+ // Sky light.
+ gSkyLight = dl;
+ dl->light.type = emit_skylight;
+
+ // Sky ambient light.
+ gAmbient = AllocDLight( dl->light.origin, false );
+ gAmbient->light.type = emit_skyambient;
+ if( g_bHDR && LightForKey( e, "_ambientHDR", gAmbient->light.intensity ) )
+ {
+ // we have a valid HDR ambient light value
+ }
+ else if ( !LightForKey( e, "_ambient", gAmbient->light.intensity ) )
+ {
+ VectorScale( dl->light.intensity, 0.5, gAmbient->light.intensity );
+ }
+ if ( g_bHDR )
+ {
+ VectorScale( gAmbient->light.intensity,
+ FloatForKeyWithDefault( e, "_AmbientScaleHDR", 1.0 ),
+ gAmbient->light.intensity );
+ }
+
+ BuildVisForLightEnvironment();
+
+ // Add sky and sky ambient lights to the list.
+ AddDLightToActiveList( gSkyLight );
+ AddDLightToActiveList( gAmbient );
+ }
+}
+
+static void ParseLightPoint( entity_t* e, directlight_t* dl )
+{
+ Vector dest;
+ GetVectorForKey (e, "origin", dest );
+ dl = AllocDLight( dest, true );
+
+ ParseLightGeneric( e, dl );
+
+ dl->light.type = emit_point;
+
+ SetLightFalloffParams(e,dl);
+}
+
+/*
+ =============
+ CreateDirectLights
+ =============
+*/
+#define DIRECT_SCALE (100.0*100.0)
+void CreateDirectLights (void)
+{
+ unsigned i;
+ CPatch *p = NULL;
+ directlight_t *dl = NULL;
+ entity_t *e = NULL;
+ char *name;
+ Vector dest;
+
+ numdlights = 0;
+
+ FreeDLights();
+
+ //
+ // surfaces
+ //
+ unsigned int uiPatchCount = g_Patches.Count();
+ for (i=0; i< uiPatchCount; i++)
+ {
+ p = &g_Patches.Element( i );
+
+ // skip parent patches
+ if (p->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ if (p->basearea < 1e-6)
+ continue;
+
+ if( VectorAvg( p->baselight ) >= dlight_threshold )
+ {
+ dl = AllocDLight( p->origin, true );
+
+ dl->light.type = emit_surface;
+ VectorCopy (p->normal, dl->light.normal);
+ Assert( VectorLength( p->normal ) > 1.0e-20 );
+ // scale intensity by number of texture instances
+ VectorScale( p->baselight, lightscale * p->area * p->scale[0] * p->scale[1] / p->basearea, dl->light.intensity );
+
+ // scale to a range that results in actual light
+ VectorScale( dl->light.intensity, DIRECT_SCALE, dl->light.intensity );
+ }
+ }
+
+ //
+ // entities
+ //
+ for (i=0 ; i<(unsigned)num_entities ; i++)
+ {
+ e = &entities[i];
+ name = ValueForKey (e, "classname");
+ if (strncmp (name, "light", 5))
+ continue;
+
+ // Light_dynamic is actually a real entity; not to be included here...
+ if (!strcmp (name, "light_dynamic"))
+ continue;
+
+ if (!strcmp (name, "light_spot"))
+ {
+ ParseLightSpot( e, dl );
+ }
+ else if (!strcmp(name, "light_environment"))
+ {
+ ParseLightEnvironment( e, dl );
+ }
+ else if (!strcmp(name, "light"))
+ {
+ ParseLightPoint( e, dl );
+ }
+ else
+ {
+ qprintf( "unsupported light entity: \"%s\"\n", name );
+ }
+ }
+
+ qprintf ("%i direct lights\n", numdlights);
+ // exit(1);
+}
+
+/*
+ =============
+ ExportDirectLightsToWorldLights
+ =============
+*/
+
+void ExportDirectLightsToWorldLights()
+{
+ directlight_t *dl;
+
+ // In case the level has already been VRADed.
+ *pNumworldlights = 0;
+
+ for (dl = activelights; dl != NULL; dl = dl->next )
+ {
+ dworldlight_t *wl = &dworldlights[(*pNumworldlights)++];
+
+ if (*pNumworldlights > MAX_MAP_WORLDLIGHTS)
+ {
+ Error("too many lights %d / %d\n", *pNumworldlights, MAX_MAP_WORLDLIGHTS );
+ }
+
+ wl->cluster = dl->light.cluster;
+ wl->type = dl->light.type;
+ wl->style = dl->light.style;
+ VectorCopy( dl->light.origin, wl->origin );
+ // FIXME: why does vrad want 0 to 255 and not 0 to 1??
+ VectorScale( dl->light.intensity, (1.0 / 255.0), wl->intensity );
+ VectorCopy( dl->light.normal, wl->normal );
+ wl->stopdot = dl->light.stopdot;
+ wl->stopdot2 = dl->light.stopdot2;
+ wl->exponent = dl->light.exponent;
+ wl->radius = dl->light.radius;
+ wl->constant_attn = dl->light.constant_attn;
+ wl->linear_attn = dl->light.linear_attn;
+ wl->quadratic_attn = dl->light.quadratic_attn;
+ wl->flags = 0;
+ }
+}
+
+/*
+ =============
+ GatherSampleLight
+ =============
+*/
+#define NORMALFORMFACTOR 40.156979 // accumuated dot products for hemisphere
+
+#define CONSTANT_DOT (.7/2)
+
+#define NSAMPLES_SUN_AREA_LIGHT 30 // number of samples to take for an
+ // non-point sun light
+
+// Helper function - gathers light from sun (emit_skylight)
+void GatherSampleSkyLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags, int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+ bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
+ bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
+
+ fltx4 dot;
+
+ if ( bIgnoreNormals )
+ dot = ReplicateX4( CONSTANT_DOT );
+ else
+ dot = NegSIMD( pNormals[0] * dl->light.normal );
+
+ dot = MaxSIMD( dot, Four_Zeros );
+ int zeroMask = TestSignSIMD ( CmpEqSIMD( dot, Four_Zeros ) );
+ if (zeroMask == 0xF)
+ return;
+
+ int nsamples = 1;
+ if ( g_SunAngularExtent > 0.0f )
+ {
+ nsamples = NSAMPLES_SUN_AREA_LIGHT;
+ if ( do_fast || force_fast )
+ nsamples /= 4;
+ }
+
+ fltx4 totalFractionVisible = Four_Zeros;
+ fltx4 fractionVisible = Four_Zeros;
+
+ DirectionalSampler_t sampler;
+
+ for ( int d = 0; d < nsamples; d++ )
+ {
+ // determine visibility of skylight
+ // serach back to see if we can hit a sky brush
+ Vector delta;
+ VectorScale( dl->light.normal, -MAX_TRACE_LENGTH, delta );
+ if ( d )
+ {
+ // jitter light source location
+ Vector ofs = sampler.NextValue();
+ ofs *= MAX_TRACE_LENGTH * g_SunAngularExtent;
+ delta += ofs;
+ }
+ FourVectors delta4;
+ delta4.DuplicateVector ( delta );
+ delta4 += pos;
+
+ TestLine_DoesHitSky ( pos, delta4, &fractionVisible, true, static_prop_index_to_ignore );
+
+ totalFractionVisible = AddSIMD ( totalFractionVisible, fractionVisible );
+ }
+
+ fltx4 seeAmount = MulSIMD ( totalFractionVisible, ReplicateX4 ( 1.0f / nsamples ) );
+ out.m_flDot[0] = MulSIMD ( dot, seeAmount );
+ out.m_flFalloff = Four_Ones;
+ out.m_flSunAmount = MulSIMD ( seeAmount, ReplicateX4( 10000.0f ) );
+ for ( int i = 1; i < normalCount; i++ )
+ {
+ if ( bIgnoreNormals )
+ out.m_flDot[i] = ReplicateX4 ( CONSTANT_DOT );
+ else
+ {
+ out.m_flDot[i] = NegSIMD( pNormals[i] * dl->light.normal );
+ out.m_flDot[i] = MulSIMD( out.m_flDot[i], seeAmount );
+ }
+ }
+}
+
+// Helper function - gathers light from ambient sky light
+void GatherSampleAmbientSkySSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags, int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+
+ bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
+ bool force_fast = ( nLFlags & GATHERLFLAGS_FORCE_FAST ) != 0;
+
+ fltx4 sumdot = Four_Zeros;
+ fltx4 ambient_intensity[NUM_BUMP_VECTS+1];
+ fltx4 possibleHitCount[NUM_BUMP_VECTS+1];
+ fltx4 dots[NUM_BUMP_VECTS+1];
+
+ for ( int i = 0; i < normalCount; i++ )
+ {
+ ambient_intensity[i] = Four_Zeros;
+ possibleHitCount[i] = Four_Zeros;
+ }
+
+ DirectionalSampler_t sampler;
+ int nsky_samples = NUMVERTEXNORMALS;
+ if (do_fast || force_fast )
+ nsky_samples /= 4;
+ else
+ nsky_samples *= g_flSkySampleScale;
+
+ for (int j = 0; j < nsky_samples; j++)
+ {
+ FourVectors anorm;
+ anorm.DuplicateVector( sampler.NextValue() );
+
+ if ( bIgnoreNormals )
+ dots[0] = ReplicateX4( CONSTANT_DOT );
+ else
+ dots[0] = NegSIMD( pNormals[0] * anorm );
+
+ fltx4 validity = CmpGtSIMD( dots[0], ReplicateX4( EQUAL_EPSILON ) );
+
+ // No possibility of anybody getting lit
+ if ( !TestSignSIMD( validity ) )
+ continue;
+
+ dots[0] = AndSIMD( validity, dots[0] );
+ sumdot = AddSIMD( dots[0], sumdot );
+ possibleHitCount[0] = AddSIMD( AndSIMD( validity, Four_Ones ), possibleHitCount[0] );
+
+ for ( int i = 1; i < normalCount; i++ )
+ {
+ if ( bIgnoreNormals )
+ dots[i] = ReplicateX4( CONSTANT_DOT );
+ else
+ dots[i] = NegSIMD( pNormals[i] * anorm );
+ fltx4 validity2 = CmpGtSIMD( dots[i], ReplicateX4 ( EQUAL_EPSILON ) );
+ dots[i] = AndSIMD( validity2, dots[i] );
+ possibleHitCount[i] = AddSIMD( AndSIMD( AndSIMD( validity, validity2 ), Four_Ones ), possibleHitCount[i] );
+ }
+
+ // search back to see if we can hit a sky brush
+ FourVectors delta = anorm;
+ delta *= -MAX_TRACE_LENGTH;
+ delta += pos;
+ FourVectors surfacePos = pos;
+ FourVectors offset = anorm;
+ offset *= -flEpsilon;
+ surfacePos -= offset;
+
+ fltx4 fractionVisible = Four_Ones;
+ TestLine_DoesHitSky( surfacePos, delta, &fractionVisible, true, static_prop_index_to_ignore );
+ for ( int i = 0; i < normalCount; i++ )
+ {
+ fltx4 addedAmount = MulSIMD( fractionVisible, dots[i] );
+ ambient_intensity[i] = AddSIMD( ambient_intensity[i], addedAmount );
+ }
+
+ }
+
+ out.m_flFalloff = Four_Ones;
+ for ( int i = 0; i < normalCount; i++ )
+ {
+ // now scale out the missing parts of the hemisphere of this bump basis vector
+ fltx4 factor = ReciprocalSIMD( possibleHitCount[0] );
+ factor = MulSIMD( factor, possibleHitCount[i] );
+ out.m_flDot[i] = MulSIMD( factor, sumdot );
+ out.m_flDot[i] = ReciprocalSIMD( out.m_flDot[i] );
+ out.m_flDot[i] = MulSIMD( ambient_intensity[i], out.m_flDot[i] );
+ }
+
+}
+
+// Helper function - gathers light from area lights, spot lights, and point lights
+void GatherSampleStandardLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags, int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+ bool bIgnoreNormals = ( nLFlags & GATHERLFLAGS_IGNORE_NORMALS ) != 0;
+
+ FourVectors src;
+ src.DuplicateVector( vec3_origin );
+
+ if (dl->facenum == -1)
+ {
+ src.DuplicateVector( dl->light.origin );
+ }
+
+ // Find light vector
+ FourVectors delta;
+ delta = src;
+ delta -= pos;
+ fltx4 dist2 = delta.length2();
+ fltx4 rpcDist = ReciprocalSqrtSIMD( dist2 );
+ delta *= rpcDist;
+ fltx4 dist = SqrtEstSIMD( dist2 );//delta.VectorNormalize();
+
+ // Compute dot
+ fltx4 dot = ReplicateX4( (float) CONSTANT_DOT );
+ if ( !bIgnoreNormals )
+ dot = delta * pNormals[0];
+ dot = MaxSIMD( Four_Zeros, dot );
+
+ // Affix dot to zero if past fade distz
+ bool bHasHardFalloff = ( dl->m_flEndFadeDistance > dl->m_flStartFadeDistance );
+ if ( bHasHardFalloff )
+ {
+ fltx4 notPastFadeDist = CmpLeSIMD ( dist, ReplicateX4 ( dl->m_flEndFadeDistance ) );
+ dot = AndSIMD( dot, notPastFadeDist ); // dot = 0 if past fade distance
+ if ( !TestSignSIMD ( notPastFadeDist ) )
+ return;
+ }
+
+ dist = MaxSIMD( dist, Four_Ones );
+ fltx4 falloffEvalDist = MinSIMD( dist, ReplicateX4( dl->m_flCapDist ) );
+
+ fltx4 constant, linear, quadratic;
+ fltx4 dot2, inCone, inFringe, mult;
+ FourVectors offset;
+
+ switch (dl->light.type)
+ {
+ case emit_point:
+ constant = ReplicateX4( dl->light.constant_attn );
+ linear = ReplicateX4( dl->light.linear_attn );
+ quadratic = ReplicateX4( dl->light.quadratic_attn );
+
+ out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
+ out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
+ break;
+
+ case emit_surface:
+ dot2 = delta * dl->light.normal;
+ dot2 = NegSIMD( dot2 );
+
+ // Light behind surface yields zero dot
+ dot2 = MaxSIMD( Four_Zeros, dot2 );
+ if ( TestSignSIMD( CmpEqSIMD( Four_Zeros, dot ) ) == 0xF )
+ return;
+
+ out.m_flFalloff = ReciprocalSIMD ( dist2 );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
+
+ // move the endpoint away from the surface by epsilon to prevent hitting the surface with the trace
+ offset.DuplicateVector ( dl->light.normal );
+ offset *= DIST_EPSILON;
+ src += offset;
+ break;
+
+ case emit_spotlight:
+ dot2 = delta * dl->light.normal;
+ dot2 = NegSIMD( dot2 );
+
+ // Affix dot2 to zero if outside light cone
+ inCone = CmpGtSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) );
+ if ( !TestSignSIMD ( inCone ) )
+ return;
+ dot = AndSIMD( inCone, dot );
+
+ constant = ReplicateX4( dl->light.constant_attn );
+ linear = ReplicateX4( dl->light.linear_attn );
+ quadratic = ReplicateX4( dl->light.quadratic_attn );
+
+ out.m_flFalloff = MulSIMD( falloffEvalDist, falloffEvalDist );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, quadratic );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, MulSIMD( linear, falloffEvalDist ) );
+ out.m_flFalloff = AddSIMD( out.m_flFalloff, constant );
+ out.m_flFalloff = ReciprocalSIMD( out.m_flFalloff );
+ out.m_flFalloff = MulSIMD( out.m_flFalloff, dot2 );
+
+ // outside the inner cone
+ inFringe = CmpLeSIMD( dot2, ReplicateX4( dl->light.stopdot ) );
+ mult = ReplicateX4( dl->light.stopdot - dl->light.stopdot2 );
+ mult = ReciprocalSIMD( mult );
+ mult = MulSIMD( mult, SubSIMD( dot2, ReplicateX4( dl->light.stopdot2 ) ) );
+ mult = MinSIMD( mult, Four_Ones );
+ mult = MaxSIMD( mult, Four_Zeros );
+
+ // pow is fixed point, so this isn't the most accurate, but it doesn't need to be
+ if ( (dl->light.exponent != 0.0f ) && ( dl->light.exponent != 1.0f ) )
+ mult = PowSIMD( mult, dl->light.exponent );
+
+ // if not in between inner and outer cones, mult by 1
+ mult = AndSIMD( inFringe, mult );
+ mult = AddSIMD( mult, AndNotSIMD( inFringe, Four_Ones ) );
+ out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
+ break;
+
+ }
+
+ // we may be in the fade region - modulate lighting by the fade curve
+ //float t = ( dist - dl->m_flStartFadeDistance ) /
+ // ( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
+ if ( bHasHardFalloff )
+ {
+ fltx4 t = ReplicateX4( dl->m_flEndFadeDistance - dl->m_flStartFadeDistance );
+ t = ReciprocalSIMD( t );
+ t = MulSIMD( t, SubSIMD( dist, ReplicateX4( dl->m_flStartFadeDistance ) ) );
+
+ // clamp t to [0...1]
+ t = MinSIMD( t, Four_Ones );
+ t = MaxSIMD( t, Four_Zeros );
+ t = SubSIMD( Four_Ones, t );
+
+ // Using QuinticInterpolatingPolynomial, SSE-ified
+ // t * t * t *( t * ( t* 6.0 - 15.0 ) + 10.0 )
+ mult = SubSIMD( MulSIMD( ReplicateX4( 6.0f ), t ), ReplicateX4( 15.0f ) );
+ mult = AddSIMD( MulSIMD( mult, t ), ReplicateX4( 10.0f ) );
+ mult = MulSIMD( MulSIMD( t, t), mult );
+ mult = MulSIMD( t, mult );
+ out.m_flFalloff = MulSIMD( mult, out.m_flFalloff );
+ }
+
+ // Raytrace for visibility function
+ fltx4 fractionVisible = Four_Ones;
+ TestLine( pos, src, &fractionVisible, static_prop_index_to_ignore);
+ dot = MulSIMD( fractionVisible, dot );
+ out.m_flDot[0] = dot;
+
+ for ( int i = 1; i < normalCount; i++ )
+ {
+ if ( bIgnoreNormals )
+ out.m_flDot[i] = ReplicateX4( (float) CONSTANT_DOT );
+ else
+ {
+ out.m_flDot[i] = pNormals[i] * delta;
+ out.m_flDot[i] = MaxSIMD( Four_Zeros, out.m_flDot[i] );
+ }
+ }
+}
+
+// returns dot product with normal and delta
+// dl - light
+// pos - position of sample
+// normal - surface normal of sample
+// out.m_flDot[] - returned dot products with light vector and each normal
+// out.m_flFalloff - amount of light falloff
+void GatherSampleLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags,
+ int static_prop_index_to_ignore,
+ float flEpsilon )
+{
+ for ( int b = 0; b < normalCount; b++ )
+ out.m_flDot[b] = Four_Zeros;
+ out.m_flFalloff = Four_Zeros;
+ out.m_flSunAmount = Four_Zeros;
+ Assert( normalCount <= (NUM_BUMP_VECTS+1) );
+
+ // skylights work fundamentally differently than normal lights
+ switch( dl->light.type )
+ {
+ case emit_skylight:
+ GatherSampleSkyLightSSE( out, dl, facenum, pos, pNormals, normalCount,
+ iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
+ break;
+ case emit_skyambient:
+ GatherSampleAmbientSkySSE( out, dl, facenum, pos, pNormals, normalCount,
+ iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
+ break;
+ case emit_point:
+ case emit_surface:
+ case emit_spotlight:
+ GatherSampleStandardLightSSE( out, dl, facenum, pos, pNormals, normalCount,
+ iThread, nLFlags, static_prop_index_to_ignore, flEpsilon );
+ break;
+ default:
+ Error ("Bad dl->light.type");
+ return;
+ }
+
+ // NOTE: Notice here that if the light is on the back side of the face
+ // (tested by checking the dot product of the face normal and the light position)
+ // we don't want it to contribute to *any* of the bumped lightmaps. It glows
+ // in disturbing ways if we don't do this.
+ out.m_flDot[0] = MaxSIMD ( out.m_flDot[0], Four_Zeros );
+ fltx4 notZero = CmpGtSIMD( out.m_flDot[0], Four_Zeros );
+ for ( int n = 1; n < normalCount; n++ )
+ {
+ out.m_flDot[n] = MaxSIMD( out.m_flDot[n], Four_Zeros );
+ out.m_flDot[n] = AndSIMD( out.m_flDot[n], notZero );
+ }
+
+}
+
+/*
+ =============
+ AddSampleToPatch
+
+ Take the sample's collected light and
+ add it back into the apropriate patch
+ for the radiosity pass.
+ =============
+*/
+void AddSampleToPatch (sample_t *s, LightingValue_t& light, int facenum)
+{
+ CPatch *patch;
+ Vector mins, maxs;
+ int i;
+
+ if (numbounce == 0)
+ return;
+ if( VectorAvg( light.m_vecLighting ) < 1)
+ return;
+
+ //
+ // fixed the sample position and normal -- need to find the equiv pos, etc to set up
+ // patches
+ //
+ if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
+ return;
+
+ float radius = sqrt( s->area ) / 2.0;
+
+ CPatch *pNextPatch = NULL;
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ if (patch->sky)
+ continue;
+
+ // skip patches with children
+ if ( patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // see if the point is in this patch (roughly)
+ WindingBounds (patch->winding, mins, maxs);
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (mins[i] > s->pos[i] + radius)
+ goto nextpatch;
+ if (maxs[i] < s->pos[i] - radius)
+ goto nextpatch;
+ }
+
+ // add the sample to the patch
+ patch->samplearea += s->area;
+ VectorMA( patch->samplelight, s->area, light.m_vecLighting, patch->samplelight );
+
+ nextpatch:;
+ }
+ // don't worry if some samples don't find a patch
+}
+
+
+void GetPhongNormal( int facenum, Vector const& spot, Vector& phongnormal )
+{
+ int j;
+ dface_t *f = &g_pFaces[facenum];
+// dplane_t *p = &dplanes[f->planenum];
+ Vector facenormal, vspot;
+
+ VectorCopy( dplanes[f->planenum].normal, facenormal );
+ VectorCopy( facenormal, phongnormal );
+
+ if ( smoothing_threshold != 1 )
+ {
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ // Calculate modified point normal for surface
+ // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
+ // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
+ // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
+ // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
+
+ for (j=0 ; j<f->numedges ; j++)
+ {
+ Vector v1, v2;
+ //int e = dsurfedges[f->firstedge + j];
+ //int e1 = dsurfedges[f->firstedge + ((j+f->numedges-1)%f->numedges)];
+ //int e2 = dsurfedges[f->firstedge + ((j+1)%f->numedges)];
+
+ //edgeshare_t *es = &edgeshare[abs(e)];
+ //edgeshare_t *es1 = &edgeshare[abs(e1)];
+ //edgeshare_t *es2 = &edgeshare[abs(e2)];
+ // dface_t *f2;
+ float a1, a2, aa, bb, ab;
+ int vert1, vert2;
+
+ Vector& n1 = fn->normal[j];
+ Vector& n2 = fn->normal[(j+1)%f->numedges];
+
+ /*
+ if (VectorCompare( n1, fn->facenormal )
+ && VectorCompare( n2, fn->facenormal) )
+ continue;
+ */
+
+ vert1 = EdgeVertex( f, j );
+ vert2 = EdgeVertex( f, j+1 );
+
+ Vector& p1 = dvertexes[vert1].point;
+ Vector& p2 = dvertexes[vert2].point;
+
+ // Build vectors from the middle of the face to the edge vertexes and the sample pos.
+ VectorSubtract( p1, face_centroids[facenum], v1 );
+ VectorSubtract( p2, face_centroids[facenum], v2 );
+ VectorSubtract( spot, face_centroids[facenum], vspot );
+ aa = DotProduct( v1, v1 );
+ bb = DotProduct( v2, v2 );
+ ab = DotProduct( v1, v2 );
+ a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
+ a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
+
+ // Test center to sample vector for inclusion between center to vertex vectors (Use dot product of vectors)
+ if ( a1 >= 0.0 && a2 >= 0.0)
+ {
+ // calculate distance from edge to pos
+ Vector temp;
+ float scale;
+
+ // Interpolate between the center and edge normals based on sample position
+ scale = 1.0 - a1 - a2;
+ VectorScale( fn->facenormal, scale, phongnormal );
+ VectorScale( n1, a1, temp );
+ VectorAdd( phongnormal, temp, phongnormal );
+ VectorScale( n2, a2, temp );
+ VectorAdd( phongnormal, temp, phongnormal );
+ Assert( VectorLength( phongnormal ) > 1.0e-20 );
+ VectorNormalize( phongnormal );
+
+ /*
+ if (a1 > 1 || a2 > 1 || a1 + a2 > 1)
+ {
+ Msg("\n%.2f %.2f\n", a1, a2 );
+ Msg("%.2f %.2f %.2f\n", v1[0], v1[1], v1[2] );
+ Msg("%.2f %.2f %.2f\n", v2[0], v2[1], v2[2] );
+ Msg("%.2f %.2f %.2f\n", vspot[0], vspot[1], vspot[2] );
+ exit(1);
+
+ a1 = 0;
+ }
+ */
+ /*
+ phongnormal[0] = (((j + 1) & 4) != 0) * 255;
+ phongnormal[1] = (((j + 1) & 2) != 0) * 255;
+ phongnormal[2] = (((j + 1) & 1) != 0) * 255;
+ */
+ return;
+ }
+ }
+ }
+}
+
+void GetPhongNormal( int facenum, FourVectors const& spot, FourVectors& phongnormal )
+{
+ int j;
+ dface_t *f = &g_pFaces[facenum];
+ // dplane_t *p = &dplanes[f->planenum];
+ Vector facenormal;
+ FourVectors vspot;
+
+ VectorCopy( dplanes[f->planenum].normal, facenormal );
+ phongnormal.DuplicateVector( facenormal );
+
+ FourVectors faceCentroid;
+ faceCentroid.DuplicateVector( face_centroids[facenum] );
+
+ if ( smoothing_threshold != 1 )
+ {
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ // Calculate modified point normal for surface
+ // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s)
+ // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal.
+ // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric)
+ // Better third attempt: generate the point normals for all vertices and do baricentric triangulation.
+
+ for ( j = 0; j < f->numedges; ++j )
+ {
+ Vector v1, v2;
+ fltx4 a1, a2;
+ float aa, bb, ab;
+ int vert1, vert2;
+
+ Vector& n1 = fn->normal[j];
+ Vector& n2 = fn->normal[(j+1)%f->numedges];
+
+ vert1 = EdgeVertex( f, j );
+ vert2 = EdgeVertex( f, j+1 );
+
+ Vector& p1 = dvertexes[vert1].point;
+ Vector& p2 = dvertexes[vert2].point;
+
+ // Build vectors from the middle of the face to the edge vertexes and the sample pos.
+ VectorSubtract( p1, face_centroids[facenum], v1 );
+ VectorSubtract( p2, face_centroids[facenum], v2 );
+ //VectorSubtract( spot, face_centroids[facenum], vspot );
+ vspot = spot;
+ vspot -= faceCentroid;
+ aa = DotProduct( v1, v1 );
+ bb = DotProduct( v2, v2 );
+ ab = DotProduct( v1, v2 );
+ //a1 = (bb * DotProduct( v1, vspot ) - ab * DotProduct( vspot, v2 )) / (aa * bb - ab * ab);
+ a1 = ReciprocalSIMD( ReplicateX4( aa * bb - ab * ab ) );
+ a1 = MulSIMD( a1, SubSIMD( MulSIMD( ReplicateX4( bb ), vspot * v1 ), MulSIMD( ReplicateX4( ab ), vspot * v2 ) ) );
+ //a2 = (DotProduct( vspot, v2 ) - a1 * ab) / bb;
+ a2 = ReciprocalSIMD( ReplicateX4( bb ) );
+ a2 = MulSIMD( a2, SubSIMD( vspot * v2, MulSIMD( a1, ReplicateX4( ab ) ) ) );
+
+ fltx4 resultMask = AndSIMD( CmpGeSIMD( a1, Four_Zeros ), CmpGeSIMD( a2, Four_Zeros ) );
+
+ if ( !TestSignSIMD( resultMask ) )
+ continue;
+
+ // Store the old phong normal to avoid overwriting already computed phong normals
+ FourVectors oldPhongNormal = phongnormal;
+
+ // calculate distance from edge to pos
+ FourVectors temp;
+ fltx4 scale;
+
+ // Interpolate between the center and edge normals based on sample position
+ scale = SubSIMD( SubSIMD( Four_Ones, a1 ), a2 );
+ phongnormal.DuplicateVector( fn->facenormal );
+ phongnormal *= scale;
+ temp.DuplicateVector( n1 );
+ temp *= a1;
+ phongnormal += temp;
+ temp.DuplicateVector( n2 );
+ temp *= a2;
+ phongnormal += temp;
+
+ // restore the old phong normals
+ phongnormal.x = AddSIMD( AndSIMD( resultMask, phongnormal.x ), AndNotSIMD( resultMask, oldPhongNormal.x ) );
+ phongnormal.y = AddSIMD( AndSIMD( resultMask, phongnormal.y ), AndNotSIMD( resultMask, oldPhongNormal.y ) );
+ phongnormal.z = AddSIMD( AndSIMD( resultMask, phongnormal.z ), AndNotSIMD( resultMask, oldPhongNormal.z ) );
+ }
+
+ phongnormal.VectorNormalize();
+ }
+}
+
+
+
+int GetVisCache( int lastoffset, int cluster, byte *pvs )
+{
+ // get the PVS for the pos to limit the number of checks
+ if ( !visdatasize )
+ {
+ memset (pvs, 255, (dvis->numclusters+7)/8 );
+ lastoffset = -1;
+ }
+ else
+ {
+ if (cluster < 0)
+ {
+ // Error, point embedded in wall
+ // sampled[0][1] = 255;
+ memset (pvs, 255, (dvis->numclusters+7)/8 );
+ lastoffset = -1;
+ }
+ else
+ {
+ int thisoffset = dvis->bitofs[ cluster ][DVIS_PVS];
+ if ( thisoffset != lastoffset )
+ {
+ if ( thisoffset == -1 )
+ {
+ Error ("visofs == -1");
+ }
+
+ DecompressVis (&dvisdata[thisoffset], pvs);
+ }
+ lastoffset = thisoffset;
+ }
+ }
+ return lastoffset;
+}
+
+
+void BuildPatchLights( int facenum );
+
+void DumpSamples( int ndxFace, facelight_t *pFaceLight )
+{
+ ThreadLock();
+
+ dface_t *pFace = &g_pFaces[ndxFace];
+ if( pFace )
+ {
+ bool bBumpped = ( ( texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ) != 0 );
+
+ for( int iStyle = 0; iStyle < 4; ++iStyle )
+ {
+ if( pFace->styles[iStyle] != 255 )
+ {
+ for ( int iBump = 0; iBump < 4; ++iBump )
+ {
+ if ( iBump == 0 || ( iBump > 0 && bBumpped ) )
+ {
+ for( int iSample = 0; iSample < pFaceLight->numsamples; ++iSample )
+ {
+ sample_t *pSample = &pFaceLight->sample[iSample];
+ WriteWinding( pFileSamples[iStyle][iBump], pSample->w, pFaceLight->light[iStyle][iBump][iSample].m_vecLighting );
+ if( bDumpNormals )
+ {
+ WriteNormal( pFileSamples[iStyle][iBump], pSample->pos, pSample->normal, 15.0f, pSample->normal * 255.0f );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ThreadUnlock();
+}
+
+
+//-----------------------------------------------------------------------------
+// Allocates light sample data
+//-----------------------------------------------------------------------------
+static inline void AllocateLightstyleSamples( facelight_t* fl, int styleIndex, int numnormals )
+{
+ for (int n = 0; n < numnormals; ++n)
+ {
+ fl->light[styleIndex][n] = ( LightingValue_t* )calloc( fl->numsamples, sizeof(LightingValue_t ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to find an existing lightstyle on a face
+//-----------------------------------------------------------------------------
+static inline int FindLightstyle( dface_t* f, int lightstyle )
+{
+ for (int k = 0; k < MAXLIGHTMAPS; k++)
+ {
+ if (f->styles[k] == lightstyle)
+ return k;
+ }
+
+ return -1;
+}
+
+static int FindOrAllocateLightstyleSamples( dface_t* f, facelight_t *fl, int lightstyle, int numnormals )
+{
+ // Search the lightstyles associated with the face for a match
+ int k;
+ for (k = 0; k < MAXLIGHTMAPS; k++)
+ {
+ if (f->styles[k] == lightstyle)
+ break;
+
+ // Found an empty entry, we can use it for a new lightstyle
+ if (f->styles[k] == 255)
+ {
+ AllocateLightstyleSamples( fl, k, numnormals );
+ f->styles[k] = lightstyle;
+ break;
+ }
+ }
+
+ // Check for overflow
+ if (k >= MAXLIGHTMAPS)
+ return -1;
+
+ return k;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the illumination point + normal for the sample
+//-----------------------------------------------------------------------------
+static void ComputeIlluminationPointAndNormalsSSE( lightinfo_t const& l, FourVectors const &pos, FourVectors const &norm, SSE_SampleInfo_t* pInfo, int numSamples )
+{
+
+ Vector v[4];
+
+ pInfo->m_Points = pos;
+ bool computeNormals = ( pInfo->m_NormalCount > 1 && ( pInfo->m_IsDispFace || !l.isflat ) );
+
+ // FIXME: move sample point off the surface a bit, this is done so that
+ // light sampling will not be affected by a bug where raycasts will
+ // intersect with the face being lit. We really should just have that
+ // logic in GatherSampleLight
+ FourVectors faceNormal;
+ faceNormal.DuplicateVector( l.facenormal );
+ pInfo->m_Points += faceNormal;
+
+ if ( pInfo->m_IsDispFace )
+ {
+ pInfo->m_PointNormals[0] = norm;
+ }
+ else if ( !l.isflat )
+ {
+ // If the face isn't flat, use a phong-based normal instead
+ FourVectors modelorg;
+ modelorg.DuplicateVector( l.modelorg );
+ FourVectors vecSample = pos;
+ vecSample -= modelorg;
+ GetPhongNormal( pInfo->m_FaceNum, vecSample, pInfo->m_PointNormals[0] );
+ }
+
+ if ( computeNormals )
+ {
+ Vector bv[4][NUM_BUMP_VECTS];
+ for ( int i = 0; i < 4; ++i )
+ {
+ // TODO: using Vec may slow things down a bit
+ GetBumpNormals( pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
+ pInfo->m_pTexInfo->textureVecsTexelsPerWorldUnits[1],
+ l.facenormal, pInfo->m_PointNormals[0].Vec( i ), bv[i] );
+ }
+ for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
+ {
+ pInfo->m_PointNormals[b+1].LoadAndSwizzle ( bv[0][b], bv[1][b], bv[2][b], bv[3][b] );
+ }
+ }
+
+ // TODO: this may slow things down a bit ( using Vec )
+ for ( int i = 0; i < 4; ++i )
+ pInfo->m_Clusters[i] = ClusterFromPoint( pos.Vec( i ) );
+}
+
+//-----------------------------------------------------------------------------
+// Iterates over all lights and computes lighting at up to 4 sample points
+//-----------------------------------------------------------------------------
+static void GatherSampleLightAt4Points( SSE_SampleInfo_t& info, int sampleIdx, int numSamples )
+{
+ SSE_sampleLightOutput_t out;
+
+ // Iterate over all direct lights and add them to the particular sample
+ for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
+ {
+ // is this lights cluster visible?
+ fltx4 dotMask = Four_Zeros;
+ bool skipLight = true;
+ for( int s = 0; s < numSamples; s++ )
+ {
+ if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
+ {
+ dotMask = SetComponentSIMD( dotMask, s, 1.0f );
+ skipLight = false;
+ }
+ }
+ if ( skipLight )
+ continue;
+
+ GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
+
+ // Apply the PVS check filter and compute falloff x dot
+ fltx4 fxdot[NUM_BUMP_VECTS + 1];
+ skipLight = true;
+ for ( int b = 0; b < info.m_NormalCount; b++ )
+ {
+ fxdot[b] = MulSIMD( out.m_flDot[b], dotMask );
+ fxdot[b] = MulSIMD( fxdot[b], out.m_flFalloff );
+ if ( !IsAllZeros( fxdot[b] ) )
+ {
+ skipLight = false;
+ }
+ }
+ if ( skipLight )
+ continue;
+
+ // Figure out the lightstyle for this particular sample
+ int lightStyleIndex = FindOrAllocateLightstyleSamples( info.m_pFace, info.m_pFaceLight,
+ dl->light.style, info.m_NormalCount );
+ if (lightStyleIndex < 0)
+ {
+ if (info.m_WarnFace != info.m_FaceNum)
+ {
+ Warning ("\nWARNING: Too many light styles on a face at (%f, %f, %f)\n",
+ info.m_Points.x.m128_f32[0], info.m_Points.y.m128_f32[0], info.m_Points.z.m128_f32[0] );
+ info.m_WarnFace = info.m_FaceNum;
+ }
+ continue;
+ }
+
+ // pLightmaps is an array of the lightmaps for each normal direction,
+ // here's where the result of the sample gathering goes
+ LightingValue_t** pLightmaps = info.m_pFaceLight->light[lightStyleIndex];
+
+ // Incremental lighting only cares about lightstyle zero
+ if( g_pIncremental && (dl->light.style == 0) )
+ {
+ for ( int i = 0; i < numSamples; i++ )
+ {
+ g_pIncremental->AddLightToFace( dl->m_IncrementalID, info.m_FaceNum, sampleIdx + i,
+ info.m_LightmapSize, SubFloat( fxdot[0], i ), info.m_iThread );
+ }
+ }
+
+ for( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ for ( int i = 0; i < numSamples; i++ )
+ {
+ pLightmaps[n][sampleIdx + i].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
+ }
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Iterates over all lights and computes lighting at a sample point
+//-----------------------------------------------------------------------------
+static void ResampleLightAt4Points( SSE_SampleInfo_t& info, int lightStyleIndex, int flags, LightingValue_t pLightmap[4][NUM_BUMP_VECTS+1] )
+{
+ SSE_sampleLightOutput_t out;
+
+ // Clear result
+ for ( int i = 0; i < 4; ++i )
+ {
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLightmap[i][n].Zero();
+ }
+ }
+
+ // Iterate over all direct lights and add them to the particular sample
+ for (directlight_t *dl = activelights; dl != NULL; dl = dl->next)
+ {
+ if ((flags & AMBIENT_ONLY) && (dl->light.type != emit_skyambient))
+ continue;
+
+ if ((flags & NON_AMBIENT_ONLY) && (dl->light.type == emit_skyambient))
+ continue;
+
+ // Only add contributions that match the lightstyle
+ Assert( lightStyleIndex <= MAXLIGHTMAPS );
+ Assert( info.m_pFace->styles[lightStyleIndex] != 255 );
+ if (dl->light.style != info.m_pFace->styles[lightStyleIndex])
+ continue;
+
+ // is this lights cluster visible?
+ fltx4 dotMask = Four_Zeros;
+ bool skipLight = true;
+ for( int s = 0; s < 4; s++ )
+ {
+ if( PVSCheck( dl->pvs, info.m_Clusters[s] ) )
+ {
+ dotMask = SetComponentSIMD( dotMask, s, 1.0f );
+ skipLight = false;
+ }
+ }
+ if ( skipLight )
+ continue;
+
+ // NOTE: Notice here that if the light is on the back side of the face
+ // (tested by checking the dot product of the face normal and the light position)
+ // we don't want it to contribute to *any* of the bumped lightmaps. It glows
+ // in disturbing ways if we don't do this.
+ GatherSampleLightSSE( out, dl, info.m_FaceNum, info.m_Points, info.m_PointNormals, info.m_NormalCount, info.m_iThread );
+
+ // Apply the PVS check filter and compute falloff x dot
+ fltx4 fxdot[NUM_BUMP_VECTS + 1];
+ for ( int b = 0; b < info.m_NormalCount; b++ )
+ {
+ fxdot[b] = MulSIMD( out.m_flFalloff, out.m_flDot[b] );
+ fxdot[b] = MulSIMD( fxdot[b], dotMask );
+ }
+
+ // Compute the contributions to each of the bumped lightmaps
+ // The first sample is for non-bumped lighting.
+ // The other sample are for bumpmapping.
+ for( int i = 0; i < 4; ++i )
+ {
+ for( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLightmap[i][n].AddLight( SubFloat( fxdot[n], i ), dl->light.intensity, SubFloat( out.m_flSunAmount, i ) );
+ }
+ }
+ }
+}
+
+bool PointsInWinding ( FourVectors const & point, winding_t *w, int &invalidBits )
+{
+ FourVectors edge, toPt, cross, testCross, p0, p1;
+ fltx4 invalidMask;
+
+ //
+ // get the first normal to test
+ //
+ p0.DuplicateVector( w->p[0] );
+ p1.DuplicateVector( w->p[1] );
+ toPt = point;
+ toPt -= p0;
+ edge = p1;
+ edge -= p0;
+ testCross = edge ^ toPt;
+ testCross.VectorNormalizeFast();
+
+ for( int ndxPt = 1; ndxPt < w->numpoints; ndxPt++ )
+ {
+ p0.DuplicateVector( w->p[ndxPt] );
+ p1.DuplicateVector( w->p[(ndxPt+1)%w->numpoints] );
+ toPt = point;
+ toPt -= p0;
+ edge = p1;
+ edge -= p0;
+ cross = edge ^ toPt;
+ cross.VectorNormalizeFast();
+
+ fltx4 dot = cross * testCross;
+ invalidMask = OrSIMD( invalidMask, CmpLtSIMD( dot, Four_Zeros ) );
+
+ invalidBits = TestSignSIMD ( invalidMask );
+ if ( invalidBits == 0xF )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Perform supersampling at a particular point
+//-----------------------------------------------------------------------------
+static int SupersampleLightAtPoint( lightinfo_t& l, SSE_SampleInfo_t& info,
+ int sampleIndex, int lightStyleIndex, LightingValue_t *pLight, int flags )
+{
+ sample_t& sample = info.m_pFaceLight->sample[sampleIndex];
+
+ // Get the position of the original sample in lightmapspace
+ Vector2D temp;
+ WorldToLuxelSpace( &l, sample.pos, temp );
+ Vector sampleLightOrigin( temp[0], temp[1], 0.0f );
+
+ // Some parameters related to supersampling
+ float sampleWidth = ( flags & NON_AMBIENT_ONLY ) ? 4 : 2;
+ float cscale = 1.0f / sampleWidth;
+ float csshift = -((sampleWidth - 1) * cscale) / 2.0;
+
+ // Clear out the light values
+ for (int i = 0; i < info.m_NormalCount; ++i )
+ pLight[i].Zero();
+
+ int subsampleCount = 0;
+
+ FourVectors superSampleNormal;
+ superSampleNormal.DuplicateVector( sample.normal );
+
+ FourVectors superSampleLightCoord;
+ FourVectors superSamplePosition;
+
+ if ( flags & NON_AMBIENT_ONLY )
+ {
+ float aRow[4];
+ for ( int coord = 0; coord < 4; ++coord )
+ aRow[coord] = csshift + coord * cscale;
+ fltx4 sseRow = LoadUnalignedSIMD( aRow );
+
+ for (int s = 0; s < 4; ++s)
+ {
+ // make sure the coordinate is inside of the sample's winding and when normalizing
+ // below use the number of samples used, not just numsamples and some of them
+ // will be skipped if they are not inside of the winding
+ superSampleLightCoord.DuplicateVector( sampleLightOrigin );
+ superSampleLightCoord.x = AddSIMD( superSampleLightCoord.x, ReplicateX4( aRow[s] ) );
+ superSampleLightCoord.y = AddSIMD( superSampleLightCoord.y, sseRow );
+
+ // Figure out where the supersample exists in the world, and make sure
+ // it lies within the sample winding
+ LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
+
+ // A winding should exist only if the sample wasn't a uniform luxel, or if g_bDumpPatches is true.
+ int invalidBits = 0;
+ if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
+ continue;
+
+ // Compute the super-sample illumination point and normal
+ // We're assuming the flat normal is the same for all supersamples
+ ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
+
+ // Resample the non-ambient light at this point...
+ LightingValue_t result[4][NUM_BUMP_VECTS+1];
+ ResampleLightAt4Points( info, lightStyleIndex, NON_AMBIENT_ONLY, result );
+
+ // Got more subsamples
+ for ( int i = 0; i < 4; i++ )
+ {
+ if ( !( ( invalidBits >> i ) & 0x1 ) )
+ {
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLight[n].AddLight( result[i][n] );
+ }
+ ++subsampleCount;
+ }
+ }
+ }
+ }
+ else
+ {
+ FourVectors superSampleOffsets;
+ superSampleOffsets.LoadAndSwizzle( Vector( csshift, csshift, 0 ), Vector( csshift, csshift + cscale, 0),
+ Vector( csshift + cscale, csshift, 0 ), Vector( csshift + cscale, csshift + cscale, 0 ) );
+ superSampleLightCoord.DuplicateVector( sampleLightOrigin );
+ superSampleLightCoord += superSampleOffsets;
+
+ LuxelSpaceToWorld( &l, superSampleLightCoord[0], superSampleLightCoord[1], superSamplePosition );
+
+ int invalidBits = 0;
+ if ( sample.w && !PointsInWinding( superSamplePosition, sample.w, invalidBits ) )
+ return 0;
+
+ ComputeIlluminationPointAndNormalsSSE( l, superSamplePosition, superSampleNormal, &info, 4 );
+
+ LightingValue_t result[4][NUM_BUMP_VECTS+1];
+ ResampleLightAt4Points( info, lightStyleIndex, AMBIENT_ONLY, result );
+
+ // Got more subsamples
+ for ( int i = 0; i < 4; i++ )
+ {
+ if ( !( ( invalidBits >> i ) & 0x1 ) )
+ {
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ pLight[n].AddLight( result[i][n] );
+ }
+ ++subsampleCount;
+ }
+ }
+ }
+
+ return subsampleCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute gradients of a lightmap
+//-----------------------------------------------------------------------------
+static void ComputeLightmapGradients( SSE_SampleInfo_t& info, bool const* pHasProcessedSample,
+ float* pIntensity, float* gradient )
+{
+ int w = info.m_LightmapWidth;
+ int h = info.m_LightmapHeight;
+ facelight_t* fl = info.m_pFaceLight;
+
+ for (int i=0 ; i<fl->numsamples ; i++)
+ {
+ // Don't supersample the same sample twice
+ if (pHasProcessedSample[i])
+ continue;
+
+ gradient[i] = 0.0f;
+ sample_t& sample = fl->sample[i];
+
+ // Choose the maximum gradient of all bumped lightmap intensities
+ for ( int n = 0; n < info.m_NormalCount; ++n )
+ {
+ int j = n * info.m_LightmapSize + sample.s + sample.t * w;
+
+ if (sample.t > 0)
+ {
+ if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1-w] ) );
+ gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-w] ) );
+ if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1-w] ) );
+ }
+ if (sample.t < h-1)
+ {
+ if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1+w] ) );
+ gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+w] ) );
+ if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1+w] ) );
+ }
+ if (sample.s > 0) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j-1] ) );
+ if (sample.s < w-1) gradient[i] = max( gradient[i], fabs( pIntensity[j] - pIntensity[j+1] ) );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ComputeLuxelIntensity...
+//-----------------------------------------------------------------------------
+static inline void ComputeLuxelIntensity( SSE_SampleInfo_t& info, int sampleIdx,
+ LightingValue_t **ppLightSamples, float* pSampleIntensity )
+{
+ // Compute a separate intensity for each
+ sample_t& sample = info.m_pFaceLight->sample[sampleIdx];
+ int destIdx = sample.s + sample.t * info.m_LightmapWidth;
+ for (int n = 0; n < info.m_NormalCount; ++n)
+ {
+ float intensity = ppLightSamples[n][sampleIdx].Intensity();
+
+ // convert to a linear perception space
+ pSampleIntensity[n * info.m_LightmapSize + destIdx] = pow( intensity / 256.0, 1.0 / 2.2 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compute the maximum intensity based on all bumped lighting
+//-----------------------------------------------------------------------------
+static void ComputeSampleIntensities( SSE_SampleInfo_t& info, LightingValue_t **ppLightSamples, float* pSampleIntensity )
+{
+ for (int i=0; i<info.m_pFaceLight->numsamples; i++)
+ {
+ ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Perform supersampling on a particular lightstyle
+//-----------------------------------------------------------------------------
+static void BuildSupersampleFaceLights( lightinfo_t& l, SSE_SampleInfo_t& info, int lightstyleIndex )
+{
+ LightingValue_t pAmbientLight[NUM_BUMP_VECTS+1];
+ LightingValue_t pDirectLight[NUM_BUMP_VECTS+1];
+
+ // This is used to make sure we don't supersample a light sample more than once
+ int processedSampleSize = info.m_LightmapSize * sizeof(bool);
+ bool* pHasProcessedSample = (bool*)stackalloc( processedSampleSize );
+ memset( pHasProcessedSample, 0, processedSampleSize );
+
+ // This is used to compute a simple gradient computation of the light samples
+ // We're going to store the maximum intensity of all bumped samples at each sample location
+ float* pGradient = (float*)stackalloc( info.m_pFaceLight->numsamples * sizeof(float) );
+ float* pSampleIntensity = (float*)stackalloc( info.m_NormalCount * info.m_LightmapSize * sizeof(float) );
+
+ // Compute the maximum intensity of all lighting associated with this lightstyle
+ // for all bumped lighting
+ LightingValue_t **ppLightSamples = info.m_pFaceLight->light[lightstyleIndex];
+ ComputeSampleIntensities( info, ppLightSamples, pSampleIntensity );
+
+ Vector *pVisualizePass = NULL;
+ if (debug_extra)
+ {
+ int visualizationSize = info.m_pFaceLight->numsamples * sizeof(Vector);
+ pVisualizePass = (Vector*)stackalloc( visualizationSize );
+ memset( pVisualizePass, 0, visualizationSize );
+ }
+
+ // What's going on here is that we're looking for large lighting discontinuities
+ // (large light intensity gradients) as a clue that we should probably be supersampling
+ // in that area. Because the supersampling operation will cause lighting changes,
+ // we've found that it's good to re-check the gradients again and see if any other
+ // areas should be supersampled as a result of the previous pass. Keep going
+ // until all the gradients are reasonable or until we hit a max number of passes
+ bool do_anotherpass = true;
+ int pass = 1;
+ while (do_anotherpass && pass <= extrapasses)
+ {
+ // Look for lighting discontinuities to see what we should be supersampling
+ ComputeLightmapGradients( info, pHasProcessedSample, pSampleIntensity, pGradient );
+
+ do_anotherpass = false;
+
+ // Now check all of the samples and supersample those which we have
+ // marked as having high gradients
+ for (int i=0 ; i<info.m_pFaceLight->numsamples; ++i)
+ {
+ // Don't supersample the same sample twice
+ if (pHasProcessedSample[i])
+ continue;
+
+ // Don't supersample if the lighting is pretty uniform near the sample
+ if (pGradient[i] < 0.0625)
+ continue;
+
+ // Joy! We're supersampling now, and we therefore must do another pass
+ // Also, we need never bother with this sample again
+ pHasProcessedSample[i] = true;
+ do_anotherpass = true;
+
+ if (debug_extra)
+ {
+ // Mark the little visualization bitmap with a color indicating
+ // which pass it was updated on.
+ pVisualizePass[i][0] = (pass & 1) * 255;
+ pVisualizePass[i][1] = (pass & 2) * 128;
+ pVisualizePass[i][2] = (pass & 4) * 64;
+ }
+
+ // Supersample the ambient light for each bump direction vector
+ int ambientSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pAmbientLight, AMBIENT_ONLY );
+
+ // Supersample the non-ambient light for each bump direction vector
+ int directSupersampleCount = SupersampleLightAtPoint( l, info, i, lightstyleIndex, pDirectLight, NON_AMBIENT_ONLY );
+
+ // Because of sampling problems, small area triangles may have no samples.
+ // In this case, just use what we already have
+ if ( ambientSupersampleCount > 0 && directSupersampleCount > 0 )
+ {
+ // Add the ambient + directional terms together, stick it back into the lightmap
+ for (int n = 0; n < info.m_NormalCount; ++n)
+ {
+ ppLightSamples[n][i].Zero();
+ ppLightSamples[n][i].AddWeighted( pDirectLight[n],1.0f / directSupersampleCount );
+ ppLightSamples[n][i].AddWeighted( pAmbientLight[n], 1.0f / ambientSupersampleCount );
+ }
+
+ // Recompute the luxel intensity based on the supersampling
+ ComputeLuxelIntensity( info, i, ppLightSamples, pSampleIntensity );
+ }
+
+ }
+
+ // We've finished another pass
+ pass++;
+ }
+
+ if (debug_extra)
+ {
+ // Copy colors representing which supersample pass the sample was messed with
+ // into the actual lighting values so we can visualize it
+ for (int i=0 ; i<info.m_pFaceLight->numsamples ; ++i)
+ {
+ for (int j = 0; j <info.m_NormalCount; ++j)
+ {
+ VectorCopy( pVisualizePass[i], ppLightSamples[j][i].m_vecLighting );
+ }
+ }
+ }
+}
+
+void InitLightinfo( lightinfo_t *pl, int facenum )
+{
+ dface_t *f;
+
+ f = &g_pFaces[facenum];
+
+ memset (pl, 0, sizeof(*pl));
+ pl->facenum = facenum;
+
+ pl->face = f;
+
+ //
+ // rotate plane
+ //
+ VectorCopy (dplanes[f->planenum].normal, pl->facenormal);
+ pl->facedist = dplanes[f->planenum].dist;
+
+ // get the origin offset for rotating bmodels
+ VectorCopy (face_offset[facenum], pl->modelorg);
+
+ CalcFaceVectors( pl );
+
+ // figure out if the surface is flat
+ pl->isflat = true;
+ if (smoothing_threshold != 1)
+ {
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ for (int j=0 ; j<f->numedges ; j++)
+ {
+ float dot = DotProduct( pl->facenormal, fn->normal[j] );
+ if (dot < 1.0 - EQUAL_EPSILON)
+ {
+ pl->isflat = false;
+ break;
+ }
+ }
+ }
+}
+
+static void InitSampleInfo( lightinfo_t const& l, int iThread, SSE_SampleInfo_t& info )
+{
+ info.m_LightmapWidth = l.face->m_LightmapTextureSizeInLuxels[0]+1;
+ info.m_LightmapHeight = l.face->m_LightmapTextureSizeInLuxels[1]+1;
+ info.m_LightmapSize = info.m_LightmapWidth * info.m_LightmapHeight;
+
+ // How many lightmaps are we going to need?
+ info.m_pTexInfo = &texinfo[l.face->texinfo];
+ info.m_NormalCount = (info.m_pTexInfo->flags & SURF_BUMPLIGHT) ? NUM_BUMP_VECTS + 1 : 1;
+ info.m_FaceNum = l.facenum;
+ info.m_pFace = l.face;
+ info.m_pFaceLight = &facelight[info.m_FaceNum];
+ info.m_IsDispFace = ValidDispFace( info.m_pFace );
+ info.m_iThread = iThread;
+ info.m_WarnFace = -1;
+
+ info.m_NumSamples = info.m_pFaceLight->numsamples;
+ info.m_NumSampleGroups = ( info.m_NumSamples & 0x3) ? ( info.m_NumSamples / 4 ) + 1 : ( info.m_NumSamples / 4 );
+
+ // initialize normals if the surface is flat
+ if (l.isflat)
+ {
+ info.m_PointNormals[0].DuplicateVector( l.facenormal );
+
+ // use facenormal along with the smooth normal to build the three bump map vectors
+ if( info.m_NormalCount > 1 )
+ {
+ Vector bumpVects[NUM_BUMP_VECTS];
+ GetBumpNormals( info.m_pTexInfo->textureVecsTexelsPerWorldUnits[0],
+ info.m_pTexInfo->textureVecsTexelsPerWorldUnits[1], l.facenormal,
+ l.facenormal, bumpVects );//&info.m_PointNormal[1] );
+
+ for ( int b = 0; b < NUM_BUMP_VECTS; ++b )
+ {
+ info.m_PointNormals[b + 1].DuplicateVector( bumpVects[b] );
+ }
+ }
+ }
+}
+
+void BuildFacelights (int iThread, int facenum)
+{
+ int i, j;
+
+ lightinfo_t l;
+ dface_t *f;
+ facelight_t *fl;
+ SSE_SampleInfo_t sampleInfo;
+ directlight_t *dl;
+ Vector spot;
+ Vector v[4], n[4];
+
+ if( g_bInterrupt )
+ return;
+
+ // FIXME: Is there a better way to do this? Like, in RunThreadsOn, for instance?
+ // Don't pay this cost unless we have to; this is super perf-critical code.
+ if (g_pIncremental)
+ {
+ // Both threads will be accessing this so it needs to be protected or else thread A
+ // will load it in and thread B will increment it but its increment will be
+ // overwritten by thread A when thread A writes it back.
+ ThreadLock();
+ ++g_iCurFace;
+ ThreadUnlock();
+ }
+
+ // some surfaces don't need lightmaps
+ f = &g_pFaces[facenum];
+ f->lightofs = -1;
+ for (j=0 ; j<MAXLIGHTMAPS ; j++)
+ f->styles[j] = 255;
+
+ // Trivial-reject the whole face?
+ if( !( g_FacesVisibleToLights[facenum>>3] & (1 << (facenum & 7)) ) )
+ return;
+
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ return; // non-lit texture
+
+ // check for patches for this face. If none it must be degenerate. Ignore.
+ if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
+ return;
+
+ fl = &facelight[facenum];
+
+ InitLightinfo( &l, facenum );
+ CalcPoints( &l, fl, facenum );
+ InitSampleInfo( l, iThread, sampleInfo );
+
+ // Allocate sample positions/normals to SSE
+ int numGroups = ( fl->numsamples & 0x3) ? ( fl->numsamples / 4 ) + 1 : ( fl->numsamples / 4 );
+
+ // always allocate style 0 lightmap
+ f->styles[0] = 0;
+ AllocateLightstyleSamples( fl, 0, sampleInfo.m_NormalCount );
+
+ // sample the lights at each sample location
+ for ( int grp = 0; grp < numGroups; ++grp )
+ {
+ int nSample = 4 * grp;
+
+ sample_t *sample = sampleInfo.m_pFaceLight->sample + nSample;
+ int numSamples = min ( 4, sampleInfo.m_pFaceLight->numsamples - nSample );
+
+ FourVectors positions;
+ FourVectors normals;
+
+ for ( int i = 0; i < 4; i++ )
+ {
+ v[i] = ( i < numSamples ) ? sample[i].pos : sample[numSamples - 1].pos;
+ n[i] = ( i < numSamples ) ? sample[i].normal : sample[numSamples - 1].normal;
+ }
+ positions.LoadAndSwizzle( v[0], v[1], v[2], v[3] );
+ normals.LoadAndSwizzle( n[0], n[1], n[2], n[3] );
+
+ ComputeIlluminationPointAndNormalsSSE( l, positions, normals, &sampleInfo, numSamples );
+
+ // Fixup sample normals in case of smooth faces
+ if ( !l.isflat )
+ {
+ for ( int i = 0; i < numSamples; i++ )
+ sample[i].normal = sampleInfo.m_PointNormals[0].Vec( i );
+ }
+
+ // Iterate over all the lights and add their contribution to this group of spots
+ GatherSampleLightAt4Points( sampleInfo, nSample, numSamples );
+ }
+
+ // Tell the incremental light manager that we're done with this face.
+ if( g_pIncremental )
+ {
+ for (dl = activelights; dl != NULL; dl = dl->next)
+ {
+ // Only deal with lightstyle 0 for incremental lighting
+ if (dl->light.style == 0)
+ g_pIncremental->FinishFace( dl->m_IncrementalID, facenum, iThread );
+ }
+
+ // Don't have to deal with patch lights (only direct lighting is used)
+ // or supersampling
+ return;
+ }
+
+ // get rid of the -extra functionality on displacement surfaces
+ if (do_extra && !sampleInfo.m_IsDispFace)
+ {
+ // For each lightstyle, perform a supersampling pass
+ for ( i = 0; i < MAXLIGHTMAPS; ++i )
+ {
+ // Stop when we run out of lightstyles
+ if (f->styles[i] == 255)
+ break;
+
+ BuildSupersampleFaceLights( l, sampleInfo, i );
+ }
+ }
+
+ if (!g_bUseMPI)
+ {
+ //
+ // This is done on the master node when MPI is used
+ //
+ BuildPatchLights( facenum );
+ }
+
+ if( g_bDumpPatches )
+ {
+ DumpSamples( facenum, fl );
+ }
+ else
+ {
+ FreeSampleWindings( fl );
+ }
+
+}
+
+void BuildPatchLights( int facenum )
+{
+ int i, k;
+
+ CPatch *patch;
+
+ dface_t *f = &g_pFaces[facenum];
+ facelight_t *fl = &facelight[facenum];
+
+ for( k = 0; k < MAXLIGHTMAPS; k++ )
+ {
+ if (f->styles[k] == 0)
+ break;
+ }
+
+ if (k >= MAXLIGHTMAPS)
+ return;
+
+ for (i = 0; i < fl->numsamples; i++)
+ {
+ AddSampleToPatch( &fl->sample[i], fl->light[k][0][i], facenum);
+ }
+
+ // check for a valid face
+ if( g_FacePatches.Element( facenum ) == g_FacePatches.InvalidIndex() )
+ return;
+
+ // push up sampled light to parents (children always exist first in the list)
+ CPatch *pNextPatch;
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches without parents
+ if( patch->parent == g_Patches.InvalidIndex() )
+// if (patch->parent == -1)
+ continue;
+
+ CPatch *parent = &g_Patches.Element( patch->parent );
+
+ parent->samplearea += patch->samplearea;
+ VectorAdd( parent->samplelight, patch->samplelight, parent->samplelight );
+ }
+
+ // average up the direct light on each patch for radiosity
+ if (numbounce > 0)
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ if (patch->samplearea)
+ {
+ float scale;
+ Vector v;
+ scale = 1.0 / patch->samplearea;
+
+ VectorScale( patch->samplelight, scale, v );
+ VectorAdd( patch->totallight.light[0], v, patch->totallight.light[0] );
+ VectorAdd( patch->directlight, v, patch->directlight );
+ }
+ }
+ }
+
+ // pull totallight from children (children always exist first in the list)
+ for( patch = &g_Patches.Element( g_FacePatches.Element( facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ if ( patch->child1 != g_Patches.InvalidIndex() )
+ {
+ float s1, s2;
+ CPatch *child1;
+ CPatch *child2;
+
+ child1 = &g_Patches.Element( patch->child1 );
+ child2 = &g_Patches.Element( patch->child2 );
+
+ s1 = child1->area / (child1->area + child2->area);
+ s2 = child2->area / (child1->area + child2->area);
+
+ VectorScale( child1->totallight.light[0], s1, patch->totallight.light[0] );
+ VectorMA( patch->totallight.light[0], s2, child2->totallight.light[0], patch->totallight.light[0] );
+
+ VectorCopy( patch->totallight.light[0], patch->directlight );
+ }
+ }
+
+ bool needsBumpmap = false;
+ if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
+ {
+ needsBumpmap = true;
+ }
+
+ // add an ambient term if desired
+ if (ambient[0] || ambient[1] || ambient[2])
+ {
+ for( int j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
+ {
+ if ( f->styles[j] == 0 )
+ {
+ for (i = 0; i < fl->numsamples; i++)
+ {
+ fl->light[j][0][i].m_vecLighting += ambient;
+ if( needsBumpmap )
+ {
+ fl->light[j][1][i].m_vecLighting += ambient;
+ fl->light[j][2][i].m_vecLighting += ambient;
+ fl->light[j][3][i].m_vecLighting += ambient;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // light from dlight_threshold and above is sent out, but the
+ // texture itself should still be full bright
+
+#if 0
+ // if( VectorAvg( g_FacePatches[facenum]->baselight ) >= dlight_threshold) // Now all lighted surfaces glow
+ {
+ for( j=0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
+ {
+ if ( f->styles[j] == 0 )
+ {
+ // BUG: shouldn't this be done for all patches on the face?
+ for (i=0 ; i<fl->numsamples ; i++)
+ {
+ // garymctchange
+ VectorAdd( fl->light[j][0][i], g_FacePatches[facenum]->baselight, fl->light[j][0][i] );
+ if( needsBumpmap )
+ {
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ VectorAdd( fl->light[j][bumpSample][i], g_FacePatches[facenum]->baselight, fl->light[j][bumpSample][i] );
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+#endif
+}
+
+
+/*
+ =============
+ PrecompLightmapOffsets
+ =============
+*/
+
+void PrecompLightmapOffsets()
+{
+ int facenum;
+ dface_t *f;
+ int lightstyles;
+ int lightdatasize = 0;
+
+ // NOTE: We store avg face light data in this lump *before* the lightmap data itself
+ // in *reverse order* of the way the lightstyles appear in the styles array.
+ for( facenum = 0; facenum < numfaces; facenum++ )
+ {
+ f = &g_pFaces[facenum];
+
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ continue; // non-lit texture
+
+ if ( dlight_map != 0 )
+ f->styles[1] = 0;
+
+ for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
+ {
+ if ( f->styles[lightstyles] == 255 )
+ break;
+ }
+
+ if ( !lightstyles )
+ continue;
+
+ // Reserve room for the avg light color data
+ lightdatasize += lightstyles * 4;
+
+ f->lightofs = lightdatasize;
+
+ bool needsBumpmap = false;
+ if( texinfo[f->texinfo].flags & SURF_BUMPLIGHT )
+ {
+ needsBumpmap = true;
+ }
+
+ int nLuxels = (f->m_LightmapTextureSizeInLuxels[0]+1) * (f->m_LightmapTextureSizeInLuxels[1]+1);
+ if( needsBumpmap )
+ {
+ lightdatasize += nLuxels * 4 * lightstyles * ( NUM_BUMP_VECTS + 1 );
+ }
+ else
+ {
+ lightdatasize += nLuxels * 4 * lightstyles;
+ }
+ }
+
+ // The incremental lighting code needs us to preserve the contents of dlightdata
+ // since it only recomposites lighting for faces that have lights that touch them.
+ if( g_pIncremental && pdlightdata->Count() )
+ return;
+
+ pdlightdata->SetSize( lightdatasize );
+}
+
+// Clamp the three values for bumped lighting such that we trade off directionality for brightness.
+static void ColorClampBumped( Vector& color1, Vector& color2, Vector& color3 )
+{
+ Vector maxs;
+ Vector *colors[3] = { &color1, &color2, &color3 };
+ maxs[0] = VectorMaximum( color1 );
+ maxs[1] = VectorMaximum( color2 );
+ maxs[2] = VectorMaximum( color3 );
+
+ // HACK! Clean this up, and add some else statements
+#define CONDITION(a,b,c) do { if( maxs[a] >= maxs[b] && maxs[b] >= maxs[c] ) { order[0] = a; order[1] = b; order[2] = c; } } while( 0 )
+
+ int order[3];
+ CONDITION(0,1,2);
+ CONDITION(0,2,1);
+ CONDITION(1,0,2);
+ CONDITION(1,2,0);
+ CONDITION(2,0,1);
+ CONDITION(2,1,0);
+
+ int i;
+ for( i = 0; i < 3; i++ )
+ {
+ float max = VectorMaximum( *colors[order[i]] );
+ if( max <= 1.0f )
+ {
+ continue;
+ }
+ // This channel is too bright. . take half of the amount that we are over and
+ // add it to the other two channel.
+ float factorToRedist = ( max - 1.0f ) / max;
+ Vector colorToRedist = factorToRedist * *colors[order[i]];
+ *colors[order[i]] -= colorToRedist;
+ colorToRedist *= 0.5f;
+ *colors[order[(i+1)%3]] += colorToRedist;
+ *colors[order[(i+2)%3]] += colorToRedist;
+ }
+
+ ColorClamp( color1 );
+ ColorClamp( color2 );
+ ColorClamp( color3 );
+
+ if( color1[0] < 0.f ) color1[0] = 0.f;
+ if( color1[1] < 0.f ) color1[1] = 0.f;
+ if( color1[2] < 0.f ) color1[2] = 0.f;
+ if( color2[0] < 0.f ) color2[0] = 0.f;
+ if( color2[1] < 0.f ) color2[1] = 0.f;
+ if( color2[2] < 0.f ) color2[2] = 0.f;
+ if( color3[0] < 0.f ) color3[0] = 0.f;
+ if( color3[1] < 0.f ) color3[1] = 0.f;
+ if( color3[2] < 0.f ) color3[2] = 0.f;
+}
+
+static void LinearToBumpedLightmap(
+ const float *linearColor,
+ const float *linearBumpColor1,
+ const float *linearBumpColor2,
+ const float *linearBumpColor3,
+ unsigned char *ret,
+ unsigned char *retBump1,
+ unsigned char *retBump2,
+ unsigned char *retBump3 )
+{
+ const Vector &linearBump1 = *( ( const Vector * )linearBumpColor1 );
+ const Vector &linearBump2 = *( ( const Vector * )linearBumpColor2 );
+ const Vector &linearBump3 = *( ( const Vector * )linearBumpColor3 );
+
+ Vector gammaGoal;
+ // gammaGoal is premultiplied by 1/overbright, which we want
+ gammaGoal[0] = LinearToVertexLight( linearColor[0] );
+ gammaGoal[1] = LinearToVertexLight( linearColor[1] );
+ gammaGoal[2] = LinearToVertexLight( linearColor[2] );
+ Vector bumpAverage = linearBump1;
+ bumpAverage += linearBump2;
+ bumpAverage += linearBump3;
+ bumpAverage *= ( 1.0f / 3.0f );
+
+ Vector correctionScale;
+ if( *( int * )&bumpAverage[0] != 0 && *( int * )&bumpAverage[1] != 0 && *( int * )&bumpAverage[2] != 0 )
+ {
+ // fast path when we know that we don't have to worry about divide by zero.
+ VectorDivide( gammaGoal, bumpAverage, correctionScale );
+// correctionScale = gammaGoal / bumpSum;
+ }
+ else
+ {
+ correctionScale.Init( 0.0f, 0.0f, 0.0f );
+ if( bumpAverage[0] != 0.0f )
+ {
+ correctionScale[0] = gammaGoal[0] / bumpAverage[0];
+ }
+ if( bumpAverage[1] != 0.0f )
+ {
+ correctionScale[1] = gammaGoal[1] / bumpAverage[1];
+ }
+ if( bumpAverage[2] != 0.0f )
+ {
+ correctionScale[2] = gammaGoal[2] / bumpAverage[2];
+ }
+ }
+ Vector correctedBumpColor1;
+ Vector correctedBumpColor2;
+ Vector correctedBumpColor3;
+ VectorMultiply( linearBump1, correctionScale, correctedBumpColor1 );
+ VectorMultiply( linearBump2, correctionScale, correctedBumpColor2 );
+ VectorMultiply( linearBump3, correctionScale, correctedBumpColor3 );
+
+ Vector check = ( correctedBumpColor1 + correctedBumpColor2 + correctedBumpColor3 ) / 3.0f;
+
+ ColorClampBumped( correctedBumpColor1, correctedBumpColor2, correctedBumpColor3 );
+
+ ret[0] = RoundFloatToByte( gammaGoal[0] * 255.0f );
+ ret[1] = RoundFloatToByte( gammaGoal[1] * 255.0f );
+ ret[2] = RoundFloatToByte( gammaGoal[2] * 255.0f );
+ retBump1[0] = RoundFloatToByte( correctedBumpColor1[0] * 255.0f );
+ retBump1[1] = RoundFloatToByte( correctedBumpColor1[1] * 255.0f );
+ retBump1[2] = RoundFloatToByte( correctedBumpColor1[2] * 255.0f );
+ retBump2[0] = RoundFloatToByte( correctedBumpColor2[0] * 255.0f );
+ retBump2[1] = RoundFloatToByte( correctedBumpColor2[1] * 255.0f );
+ retBump2[2] = RoundFloatToByte( correctedBumpColor2[2] * 255.0f );
+ retBump3[0] = RoundFloatToByte( correctedBumpColor3[0] * 255.0f );
+ retBump3[1] = RoundFloatToByte( correctedBumpColor3[1] * 255.0f );
+ retBump3[2] = RoundFloatToByte( correctedBumpColor3[2] * 255.0f );
+}
+
+//-----------------------------------------------------------------------------
+// Convert a RGBExp32 to a RGBA8888
+// This matches the engine's conversion, so the lighting result is consistent.
+//-----------------------------------------------------------------------------
+void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst )
+{
+ Vector linearColor;
+ Vector vertexColor;
+
+ // convert from ColorRGBExp32 to linear space
+ linearColor[0] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->r, ((ColorRGBExp32 *)pSrc)->exponent );
+ linearColor[1] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->g, ((ColorRGBExp32 *)pSrc)->exponent );
+ linearColor[2] = TexLightToLinear( ((ColorRGBExp32 *)pSrc)->b, ((ColorRGBExp32 *)pSrc)->exponent );
+
+ // convert from linear space to lightmap space
+ // cannot use mathlib routine directly because it doesn't match
+ // the colorspace version found in the engine, which *is* the same sequence here
+ vertexColor[0] = LinearToVertexLight( linearColor[0] );
+ vertexColor[1] = LinearToVertexLight( linearColor[1] );
+ vertexColor[2] = LinearToVertexLight( linearColor[2] );
+
+ // this is really a color normalization with a floor
+ ColorClamp( vertexColor );
+
+ // final [0..255] scale
+ pDst[0] = RoundFloatToByte( vertexColor[0] * 255.0f );
+ pDst[1] = RoundFloatToByte( vertexColor[1] * 255.0f );
+ pDst[2] = RoundFloatToByte( vertexColor[2] * 255.0f );
+ pDst[3] = 255;
+}
+
diff --git a/mp/src/utils/vrad/lightmap.h b/mp/src/utils/vrad/lightmap.h new file mode 100644 index 00000000..0912c43f --- /dev/null +++ b/mp/src/utils/vrad/lightmap.h @@ -0,0 +1,141 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef LIGHTMAP_H
+#define LIGHTMAP_H
+#pragma once
+
+#include "mathlib/bumpvects.h"
+#include "bsplib.h"
+
+typedef struct
+{
+ dface_t *faces[2];
+ Vector interface_normal;
+ qboolean coplanar;
+} edgeshare_t;
+
+extern edgeshare_t edgeshare[MAX_MAP_EDGES];
+
+
+//==============================================
+
+// This is incremented each time BuildFaceLights and FinalLightFace
+// are called. It's used for a status bar in WorldCraft.
+extern int g_iCurFace;
+
+extern int vertexref[MAX_MAP_VERTS];
+extern int *vertexface[MAX_MAP_VERTS];
+
+struct faceneighbor_t
+{
+ int numneighbors; // neighboring faces that share vertices
+ int *neighbor; // neighboring face list (max of 64)
+
+ Vector *normal; // adjusted normal per vertex
+ Vector facenormal; // face normal
+
+ bool bHasDisp; // is this surface a displacement surface???
+};
+
+extern faceneighbor_t faceneighbor[MAX_MAP_FACES];
+
+//==============================================
+
+
+struct sample_t
+{
+ // in local luxel space
+ winding_t *w;
+ int s, t;
+ Vector2D coord;
+ Vector2D mins;
+ Vector2D maxs;
+ // in world units
+ Vector pos;
+ Vector normal;
+ float area;
+};
+
+struct facelight_t
+{
+ // irregularly shaped light sample data, clipped by face and luxel grid
+ int numsamples;
+ sample_t *sample;
+ LightingValue_t *light[MAXLIGHTMAPS][NUM_BUMP_VECTS+1]; // result of direct illumination, indexed by sample
+
+ // regularly spaced lightmap grid
+ int numluxels;
+ Vector *luxel; // world space position of luxel
+ Vector *luxelNormals; // world space normal of luxel
+ float worldAreaPerLuxel;
+};
+
+extern directlight_t *activelights;
+extern directlight_t *freelights;
+
+extern facelight_t facelight[MAX_MAP_FACES];
+extern int numdlights;
+
+
+//==============================================
+
+struct lightinfo_t
+{
+ vec_t facedist;
+ Vector facenormal;
+
+ Vector facemid; // world coordinates of center
+
+ Vector modelorg; // for origined bmodels
+
+ Vector luxelOrigin;
+ Vector worldToLuxelSpace[2]; // s = (world - luxelOrigin) . worldToLuxelSpace[0], t = (world - luxelOrigin) . worldToLuxelSpace[1]
+ Vector luxelToWorldSpace[2]; // world = luxelOrigin + s * luxelToWorldSpace[0] + t * luxelToWorldSpace[1]
+
+ int facenum;
+ dface_t *face;
+
+ int isflat;
+ int hasbumpmap;
+};
+
+struct SSE_SampleInfo_t
+{
+ int m_FaceNum;
+ int m_WarnFace;
+ dface_t *m_pFace;
+ facelight_t *m_pFaceLight;
+ int m_LightmapWidth;
+ int m_LightmapHeight;
+ int m_LightmapSize;
+ int m_NormalCount;
+ int m_iThread;
+ texinfo_t *m_pTexInfo;
+ bool m_IsDispFace;
+
+ int m_NumSamples;
+ int m_NumSampleGroups;
+ int m_Clusters[4];
+ FourVectors m_Points;
+ FourVectors m_PointNormals[ NUM_BUMP_VECTS + 1 ];
+};
+
+extern void InitLightinfo( lightinfo_t *l, int facenum );
+
+void FreeDLights();
+
+void ExportDirectLightsToWorldLights();
+
+
+#endif // LIGHTMAP_H
diff --git a/mp/src/utils/vrad/macro_texture.cpp b/mp/src/utils/vrad/macro_texture.cpp new file mode 100644 index 00000000..8511d979 --- /dev/null +++ b/mp/src/utils/vrad/macro_texture.cpp @@ -0,0 +1,166 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "tier1/strtools.h"
+#include "macro_texture.h"
+#include "bsplib.h"
+#include "cmdlib.h"
+#include "vtf/vtf.h"
+#include "tier1/utldict.h"
+#include "tier1/utlbuffer.h"
+#include "bitmap/imageformat.h"
+
+
+class CMacroTextureData
+{
+public:
+ int m_Width, m_Height;
+ CUtlMemory<unsigned char> m_ImageData;
+};
+
+
+CMacroTextureData *g_pGlobalMacroTextureData = NULL;
+
+// Which macro texture each map face uses.
+static CUtlDict<CMacroTextureData*, int> g_MacroTextureLookup; // Stores a list of unique macro textures.
+static CUtlVector<CMacroTextureData*> g_FaceMacroTextures; // Which macro texture each face wants to use.
+static Vector g_MacroWorldMins, g_MacroWorldMaxs;
+
+
+CMacroTextureData* FindMacroTexture( const char *pFilename )
+{
+ int index = g_MacroTextureLookup.Find( pFilename );
+ if ( g_MacroTextureLookup.IsValidIndex( index ) )
+ return g_MacroTextureLookup[index];
+ else
+ return NULL;
+}
+
+
+CMacroTextureData* LoadMacroTextureFile( const char *pFilename )
+{
+ FileHandle_t hFile = g_pFileSystem->Open( pFilename, "rb" );
+ if ( hFile == FILESYSTEM_INVALID_HANDLE )
+ return NULL;
+
+ // Read the file in.
+ CUtlVector<char> tempData;
+ tempData.SetSize( g_pFileSystem->Size( hFile ) );
+ g_pFileSystem->Read( tempData.Base(), tempData.Count(), hFile );
+ g_pFileSystem->Close( hFile );
+
+
+ // Now feed the data into a CUtlBuffer (great...)
+ CUtlBuffer buf;
+ buf.Put( tempData.Base(), tempData.Count() );
+
+
+ // Now make a texture out of it.
+ IVTFTexture *pTex = CreateVTFTexture();
+ if ( !pTex->Unserialize( buf ) )
+ Error( "IVTFTexture::Unserialize( %s ) failed.", pFilename );
+
+ pTex->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false ); // Get it in a format we like.
+
+
+ // Now convert to a CMacroTextureData.
+ CMacroTextureData *pData = new CMacroTextureData;
+ pData->m_Width = pTex->Width();
+ pData->m_Height = pTex->Height();
+ pData->m_ImageData.EnsureCapacity( pData->m_Width * pData->m_Height * 4 );
+ memcpy( pData->m_ImageData.Base(), pTex->ImageData(), pData->m_Width * pData->m_Height * 4 );
+
+ DestroyVTFTexture( pTex );
+
+ Msg( "-- LoadMacroTextureFile: %s\n", pFilename );
+ return pData;
+}
+
+
+void InitMacroTexture( const char *pBSPFilename )
+{
+ // Get the world bounds (same ones used by minimaps and level designers know how to use).
+ int i = 0;
+ for (i; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if( !strcmp(pEntity, "worldspawn") )
+ {
+ GetVectorForKey( &entities[i], "world_mins", g_MacroWorldMins );
+ GetVectorForKey( &entities[i], "world_maxs", g_MacroWorldMaxs );
+ break;
+ }
+ }
+
+ if ( i == num_entities )
+ {
+ Warning( "MaskOnMacroTexture: can't find worldspawn" );
+ return;
+ }
+
+
+ // Load the macro texture that is mapped onto everything.
+ char mapName[512], vtfFilename[512];
+ Q_FileBase( pBSPFilename, mapName, sizeof( mapName ) );
+ Q_snprintf( vtfFilename, sizeof( vtfFilename ), "materials/macro/%s/base.vtf", mapName );
+ g_pGlobalMacroTextureData = LoadMacroTextureFile( vtfFilename );
+
+
+ // Now load the macro texture for each face.
+ g_FaceMacroTextures.SetSize( numfaces );
+ for ( int iFace=0; iFace < numfaces; iFace++ )
+ {
+ g_FaceMacroTextures[iFace] = NULL;
+
+ if ( iFace < g_FaceMacroTextureInfos.Count() )
+ {
+ unsigned short stringID = g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID;
+ if ( stringID != 0xFFFF )
+ {
+ const char *pMacroTextureName = &g_TexDataStringData[ g_TexDataStringTable[stringID] ];
+ Q_snprintf( vtfFilename, sizeof( vtfFilename ), "%smaterials/%s.vtf", gamedir, pMacroTextureName );
+
+ g_FaceMacroTextures[iFace] = FindMacroTexture( vtfFilename );
+ if ( !g_FaceMacroTextures[iFace] )
+ {
+ g_FaceMacroTextures[iFace] = LoadMacroTextureFile( vtfFilename );
+ if ( g_FaceMacroTextures[iFace] )
+ {
+ g_MacroTextureLookup.Insert( vtfFilename, g_FaceMacroTextures[iFace] );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+inline Vector SampleMacroTexture( const CMacroTextureData *t, const Vector &vWorldPos )
+{
+ int ix = (int)RemapVal( vWorldPos.x, g_MacroWorldMins.x, g_MacroWorldMaxs.x, 0, t->m_Width-0.00001 );
+ int iy = (int)RemapVal( vWorldPos.y, g_MacroWorldMins.y, g_MacroWorldMaxs.y, 0, t->m_Height-0.00001 );
+ ix = clamp( ix, 0, t->m_Width-1 );
+ iy = t->m_Height - 1 - clamp( iy, 0, t->m_Height-1 );
+
+ const unsigned char *pInputColor = &t->m_ImageData[(iy*t->m_Width + ix) * 4];
+ return Vector( pInputColor[0] / 255.0, pInputColor[1] / 255.0, pInputColor[2] / 255.0 );
+}
+
+
+void ApplyMacroTextures( int iFace, const Vector &vWorldPos, Vector &outLuxel )
+{
+ // Add the global macro texture.
+ Vector vGlobal;
+ if ( g_pGlobalMacroTextureData )
+ outLuxel *= SampleMacroTexture( g_pGlobalMacroTextureData, vWorldPos );
+
+ // Now add the per-material macro texture.
+ if ( g_FaceMacroTextures[iFace] )
+ outLuxel *= SampleMacroTexture( g_FaceMacroTextures[iFace], vWorldPos );
+}
+
+
+
diff --git a/mp/src/utils/vrad/macro_texture.h b/mp/src/utils/vrad/macro_texture.h new file mode 100644 index 00000000..249c2474 --- /dev/null +++ b/mp/src/utils/vrad/macro_texture.h @@ -0,0 +1,24 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MACRO_TEXTURE_H
+#define MACRO_TEXTURE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "mathlib/vector.h"
+
+
+// The macro texture looks for a TGA file with the same name as the BSP file and in
+// the same directory. If it finds one, it maps this texture onto the world dimensions
+// (in the worldspawn entity) and masks all lightmaps with it.
+void InitMacroTexture( const char *pBSPFilename );
+void ApplyMacroTextures( int iFace, const Vector &vWorldPos, Vector &outLuxel );
+
+
+#endif // MACRO_TEXTURE_H
diff --git a/mp/src/utils/vrad/mpivrad.cpp b/mp/src/utils/vrad/mpivrad.cpp new file mode 100644 index 00000000..d54dfaeb --- /dev/null +++ b/mp/src/utils/vrad/mpivrad.cpp @@ -0,0 +1,496 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// mpivrad.cpp
+//
+
+#include <windows.h>
+#include <conio.h>
+#include "vrad.h"
+#include "physdll.h"
+#include "lightmap.h"
+#include "tier1/strtools.h"
+#include "radial.h"
+#include "utlbuffer.h"
+#include "pacifier.h"
+#include "messbuf.h"
+#include "bsplib.h"
+#include "consolewnd.h"
+#include "vismat.h"
+#include "vmpi_filesystem.h"
+#include "vmpi_dispatch.h"
+#include "utllinkedlist.h"
+#include "vmpi.h"
+#include "mpi_stats.h"
+#include "vmpi_distribute_work.h"
+#include "vmpi_tools_shared.h"
+
+
+
+
+CUtlVector<char> g_LightResultsFilename;
+
+
+extern int total_transfer;
+extern int max_transfer;
+
+extern void BuildVisLeafs(int);
+extern void BuildPatchLights( int facenum );
+
+
+// Handle VRAD packets.
+bool VRAD_DispatchFn( MessageBuffer *pBuf, int iSource, int iPacketID )
+{
+ switch( pBuf->data[1] )
+ {
+ case VMPI_SUBPACKETID_PLIGHTDATA_RESULTS:
+ {
+ const char *pFilename = &pBuf->data[2];
+ g_LightResultsFilename.CopyArray( pFilename, strlen( pFilename ) + 1 );
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+CDispatchReg g_VRADDispatchReg( VMPI_VRAD_PACKET_ID, VRAD_DispatchFn ); // register to handle the messages we want
+CDispatchReg g_DistributeWorkReg( VMPI_DISTRIBUTEWORK_PACKETID, DistributeWorkDispatch );
+
+
+
+void VRAD_SetupMPI( int &argc, char **&argv )
+{
+ CmdLib_AtCleanup( VMPI_Stats_Term );
+
+ //
+ // Preliminary check -mpi flag
+ //
+ if ( !VMPI_FindArg( argc, argv, "-mpi", "" ) && !VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Worker ), "" ) )
+ return;
+
+ // Force local mode?
+ VMPIRunMode mode;
+ if ( VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Local ), "" ) )
+ mode = VMPI_RUN_LOCAL;
+ else
+ mode = VMPI_RUN_NETWORKED;
+
+ VMPI_Stats_InstallSpewHook();
+
+ //
+ // Extract mpi specific arguments
+ //
+ Msg( "Initializing VMPI...\n" );
+ if ( !VMPI_Init(
+ argc,
+ argv,
+ "dependency_info_vrad.txt",
+ HandleMPIDisconnect,
+ mode
+ ) )
+ {
+ Error( "MPI_Init failed." );
+ }
+
+ StatsDB_InitStatsDatabase( argc, argv, "dbinfo_vrad.txt" );
+}
+
+
+//-----------------------------------------
+//
+// Run BuildFaceLights across all available processing nodes
+// and collect the results.
+//
+
+CCycleCount g_CPUTime;
+
+
+template<class T> void WriteValues( MessageBuffer *pmb, T const *pSrc, int nNumValues)
+{
+ pmb->write(pSrc, sizeof( pSrc[0]) * nNumValues );
+}
+
+template<class T> int ReadValues( MessageBuffer *pmb, T *pDest, int nNumValues)
+{
+ return pmb->read( pDest, sizeof( pDest[0]) * nNumValues );
+}
+
+
+//--------------------------------------------------
+// Serialize face data
+void SerializeFace( MessageBuffer * pmb, int facenum )
+{
+ int i, n;
+
+ dface_t * f = &g_pFaces[facenum];
+ facelight_t * fl = &facelight[facenum];
+
+ pmb->write(f, sizeof(dface_t));
+ pmb->write(fl, sizeof(facelight_t));
+
+ WriteValues( pmb, fl->sample, fl->numsamples);
+
+ //
+ // Write the light information
+ //
+ for (i=0; i<MAXLIGHTMAPS; ++i) {
+ for (n=0; n<NUM_BUMP_VECTS+1; ++n) {
+ if (fl->light[i][n])
+ {
+ WriteValues( pmb, fl->light[i][n], fl->numsamples);
+ }
+ }
+ }
+
+ if (fl->luxel)
+ WriteValues( pmb, fl->luxel, fl->numluxels);
+
+ if (fl->luxelNormals)
+ WriteValues( pmb, fl->luxelNormals, fl->numluxels);
+}
+
+//--------------------------------------------------
+// UnSerialize face data
+//
+void UnSerializeFace( MessageBuffer * pmb, int facenum, int iSource )
+{
+ int i, n;
+
+ dface_t * f = &g_pFaces[facenum];
+ facelight_t * fl = &facelight[facenum];
+
+ if (pmb->read(f, sizeof(dface_t)) < 0)
+ Error("UnSerializeFace - invalid dface_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+
+ if (pmb->read(fl, sizeof(facelight_t)) < 0)
+ Error("UnSerializeFace - invalid facelight_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+
+ fl->sample = (sample_t *) calloc(fl->numsamples, sizeof(sample_t));
+ if (pmb->read(fl->sample, sizeof(sample_t) * fl->numsamples) < 0)
+ Error("UnSerializeFace - invalid sample_t from %s (mb len: %d, offset: %d, fl->numsamples: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset(), fl->numsamples );
+
+ //
+ // Read the light information
+ //
+ for (i=0; i<MAXLIGHTMAPS; ++i) {
+ for (n=0; n<NUM_BUMP_VECTS+1; ++n) {
+ if (fl->light[i][n])
+ {
+ fl->light[i][n] = (LightingValue_t *) calloc( fl->numsamples, sizeof(LightingValue_t ) );
+ if ( ReadValues( pmb, fl->light[i][n], fl->numsamples) < 0)
+ Error("UnSerializeFace - invalid fl->light from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+ }
+ }
+ }
+
+ if (fl->luxel) {
+ fl->luxel = (Vector *) calloc(fl->numluxels, sizeof(Vector));
+ if (ReadValues( pmb, fl->luxel, fl->numluxels) < 0)
+ Error("UnSerializeFace - invalid fl->luxel from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+ }
+
+ if (fl->luxelNormals) {
+ fl->luxelNormals = (Vector *) calloc(fl->numluxels, sizeof( Vector ));
+ if ( ReadValues( pmb, fl->luxelNormals, fl->numluxels) < 0)
+ Error("UnSerializeFace - invalid fl->luxelNormals from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() );
+ }
+
+}
+
+
+void MPI_ReceiveFaceResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ UnSerializeFace( pBuf, iWorkUnit, iWorker );
+}
+
+
+void MPI_ProcessFaces( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf )
+{
+ // Do BuildFacelights on the face.
+ CTimeAdder adder( &g_CPUTime );
+
+ BuildFacelights( iThread, iWorkUnit );
+
+ // Send the results.
+ if ( pBuf )
+ {
+ SerializeFace( pBuf, iWorkUnit );
+ }
+}
+
+
+void RunMPIBuildFacelights()
+{
+ g_CPUTime.Init();
+
+ Msg( "%-20s ", "BuildFaceLights:" );
+ if ( g_bMPIMaster )
+ {
+ StartPacifier("");
+ }
+
+ VMPI_SetCurrentStage( "RunMPIBuildFaceLights" );
+ double elapsed = DistributeWork(
+ numfaces,
+ VMPI_DISTRIBUTEWORK_PACKETID,
+ MPI_ProcessFaces,
+ MPI_ReceiveFaceResults );
+
+ if ( g_bMPIMaster )
+ {
+ EndPacifier(false);
+ Msg( " (%d)\n", (int)elapsed );
+ }
+
+ if ( g_bMPIMaster )
+ {
+ //
+ // BuildPatchLights is normally called from BuildFacelights(),
+ // but in MPI mode we have the master do the calculation
+ // We might be able to speed this up by doing while the master
+ // is idling in the above loop. Wouldn't want to slow down the
+ // handing out of work - maybe another thread?
+ //
+ for ( int i=0; i < numfaces; ++i )
+ {
+ BuildPatchLights(i);
+ }
+ }
+ else
+ {
+ if ( g_iVMPIVerboseLevel >= 1 )
+ Msg( "\n\n%.1f%% CPU utilization during BuildFaceLights\n\n", ( g_CPUTime.GetSeconds() * 100 / elapsed ) );
+ }
+}
+
+
+//-----------------------------------------
+//
+// Run BuildVisLeafs across all available processing nodes
+// and collect the results.
+//
+
+// This function is called when the master receives results back from a worker.
+void MPI_ReceiveVisLeafsResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ int patchesInCluster = 0;
+
+ pBuf->read(&patchesInCluster, sizeof(patchesInCluster));
+
+ for ( int k=0; k < patchesInCluster; ++k )
+ {
+ int patchnum = 0;
+ pBuf->read(&patchnum, sizeof(patchnum));
+
+ CPatch * patch = &g_Patches[patchnum];
+ int numtransfers;
+ pBuf->read( &numtransfers, sizeof(numtransfers) );
+ patch->numtransfers = numtransfers;
+ if (numtransfers)
+ {
+ patch->transfers = new transfer_t[numtransfers];
+ pBuf->read(patch->transfers, numtransfers * sizeof(transfer_t));
+ }
+
+ total_transfer += numtransfers;
+ if (max_transfer < numtransfers)
+ max_transfer = numtransfers;
+ }
+}
+
+
+// Temporary variables used during callbacks. If we're going to be threadsafe, these
+// should go in a structure and get passed around.
+class CVMPIVisLeafsData
+{
+public:
+ MessageBuffer *m_pVisLeafsMB;
+ int m_nPatchesInCluster;
+ transfer_t *m_pBuildVisLeafsTransfers;
+};
+
+CVMPIVisLeafsData g_VMPIVisLeafsData[MAX_TOOL_THREADS+1];
+
+
+
+// This is called by BuildVisLeafs_Cluster every time it finishes a patch.
+// The results are appended to g_VisLeafsMB and sent back to the master when all clusters are done.
+void MPI_AddPatchData( int iThread, int patchnum, CPatch *patch )
+{
+ CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread];
+ if ( pData->m_pVisLeafsMB )
+ {
+ // Add in results for this patch
+ ++pData->m_nPatchesInCluster;
+ pData->m_pVisLeafsMB->write(&patchnum, sizeof(patchnum));
+ pData->m_pVisLeafsMB->write(&patch->numtransfers, sizeof(patch->numtransfers));
+ pData->m_pVisLeafsMB->write( patch->transfers, patch->numtransfers * sizeof(transfer_t) );
+ }
+}
+
+
+// This handles a work unit sent by the master. Each work unit here is a
+// list of clusters.
+void MPI_ProcessVisLeafs( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf )
+{
+ CTimeAdder adder( &g_CPUTime );
+
+ CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread];
+ int iCluster = iWorkUnit;
+
+ // Start this cluster.
+ pData->m_nPatchesInCluster = 0;
+ pData->m_pVisLeafsMB = pBuf;
+
+ // Write a temp value in there. We overwrite it later.
+ int iSavePos = 0;
+ if ( pBuf )
+ {
+ iSavePos = pBuf->getLen();
+ pBuf->write( &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) );
+ }
+
+ // Collect the results in MPI_AddPatchData.
+ BuildVisLeafs_Cluster( iThread, pData->m_pBuildVisLeafsTransfers, iCluster, MPI_AddPatchData );
+
+ // Now send the results back..
+ if ( pBuf )
+ {
+ pBuf->update( iSavePos, &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) );
+ pData->m_pVisLeafsMB = NULL;
+ }
+}
+
+
+void RunMPIBuildVisLeafs()
+{
+ g_CPUTime.Init();
+
+ Msg( "%-20s ", "BuildVisLeafs :" );
+ if ( g_bMPIMaster )
+ {
+ StartPacifier("");
+ }
+
+ memset( g_VMPIVisLeafsData, 0, sizeof( g_VMPIVisLeafsData ) );
+ if ( !g_bMPIMaster || VMPI_GetActiveWorkUnitDistributor() == k_eWorkUnitDistributor_SDK )
+ {
+ // Allocate space for the transfers for each thread.
+ for ( int i=0; i < numthreads; i++ )
+ {
+ g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers = BuildVisLeafs_Start();
+ }
+ }
+
+ //
+ // Slaves ask for work via GetMPIBuildVisLeafWork()
+ // Results are returned in BuildVisRow()
+ //
+ VMPI_SetCurrentStage( "RunMPIBuildVisLeafs" );
+
+ double elapsed = DistributeWork(
+ dvis->numclusters,
+ VMPI_DISTRIBUTEWORK_PACKETID,
+ MPI_ProcessVisLeafs,
+ MPI_ReceiveVisLeafsResults );
+
+ // Free the transfers from each thread.
+ for ( int i=0; i < numthreads; i++ )
+ {
+ if ( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers )
+ BuildVisLeafs_End( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers );
+ }
+
+ if ( g_bMPIMaster )
+ {
+ EndPacifier(false);
+ Msg( " (%d)\n", (int)elapsed );
+ }
+ else
+ {
+ if ( g_iVMPIVerboseLevel >= 1 )
+ Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
+ }
+}
+
+void VMPI_DistributeLightData()
+{
+ if ( !g_bUseMPI )
+ return;
+
+ if ( g_bMPIMaster )
+ {
+ const char *pVirtualFilename = "--plightdata--";
+
+ CUtlBuffer lightFaceData;
+
+ // write out the light data
+ lightFaceData.EnsureCapacity( pdlightdata->Count() + (numfaces * (MAXLIGHTMAPS+sizeof(int))) );
+ Q_memcpy( lightFaceData.PeekPut(), pdlightdata->Base(), pdlightdata->Count() );
+ lightFaceData.SeekPut( CUtlBuffer::SEEK_HEAD, pdlightdata->Count() );
+
+ // write out the relevant face info into the stream
+ for ( int i = 0; i < numfaces; i++ )
+ {
+ for ( int j = 0; j < MAXLIGHTMAPS; j++ )
+ {
+ lightFaceData.PutChar(g_pFaces[i].styles[j]);
+ }
+ lightFaceData.PutInt(g_pFaces[i].lightofs);
+ }
+ VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, lightFaceData.Base(), lightFaceData.TellMaxPut() );
+
+ char cPacketID[2] = { VMPI_VRAD_PACKET_ID, VMPI_SUBPACKETID_PLIGHTDATA_RESULTS };
+ VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT );
+ }
+ else
+ {
+ VMPI_SetCurrentStage( "VMPI_DistributeLightData" );
+
+ // Wait until we've received the filename from the master.
+ while ( g_LightResultsFilename.Count() == 0 )
+ {
+ VMPI_DispatchNextMessage();
+ }
+
+ // Open
+ FileHandle_t fp = g_pFileSystem->Open( g_LightResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID );
+ if ( !fp )
+ Error( "Can't open '%s' to read lighting info.", g_LightResultsFilename.Base() );
+
+ int size = g_pFileSystem->Size( fp );
+ int faceSize = (numfaces*(MAXLIGHTMAPS+sizeof(int)));
+
+ if ( size > faceSize )
+ {
+ int lightSize = size - faceSize;
+ CUtlBuffer faceData;
+ pdlightdata->EnsureCount( lightSize );
+ faceData.EnsureCapacity( faceSize );
+
+ g_pFileSystem->Read( pdlightdata->Base(), lightSize, fp );
+ g_pFileSystem->Read( faceData.Base(), faceSize, fp );
+ g_pFileSystem->Close( fp );
+
+ faceData.SeekPut( CUtlBuffer::SEEK_HEAD, faceSize );
+
+ // write out the face data
+ for ( int i = 0; i < numfaces; i++ )
+ {
+ for ( int j = 0; j < MAXLIGHTMAPS; j++ )
+ {
+ g_pFaces[i].styles[j] = faceData.GetChar();
+ }
+ g_pFaces[i].lightofs = faceData.GetInt();
+ }
+ }
+ }
+}
+
+
diff --git a/mp/src/utils/vrad/mpivrad.h b/mp/src/utils/vrad/mpivrad.h new file mode 100644 index 00000000..990b3df8 --- /dev/null +++ b/mp/src/utils/vrad/mpivrad.h @@ -0,0 +1,36 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MPIVRAD_H
+#define MPIVRAD_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#define VMPI_VRAD_PACKET_ID 1
+ // Sub packet IDs.
+ #define VMPI_SUBPACKETID_VIS_LEAFS 0
+ #define VMPI_SUBPACKETID_BUILDFACELIGHTS 1
+ #define VMPI_SUBPACKETID_PLIGHTDATA_RESULTS 2
+
+// DistributeWork owns this packet ID.
+#define VMPI_DISTRIBUTEWORK_PACKETID 2
+
+
+// Called first thing in the exe.
+void VRAD_SetupMPI( int &argc, char **&argv );
+
+void RunMPIBuildFacelights(void);
+void RunMPIBuildVisLeafs(void);
+void VMPI_DistributeLightData();
+
+// This handles disconnections. They're usually not fatal for the master.
+void HandleMPIDisconnect( int procID );
+
+
+#endif // MPIVRAD_H
diff --git a/mp/src/utils/vrad/notes.txt b/mp/src/utils/vrad/notes.txt new file mode 100644 index 00000000..f0ca18b3 --- /dev/null +++ b/mp/src/utils/vrad/notes.txt @@ -0,0 +1,22 @@ +//=============================================================================
+// CHARLIE:
+
+DONE: just rename files from .c to .cpp in source safe so history is kept
+converting files of to .cpp files -- just get stuff recompiling first
+make sure all common files and external dependencies handle .cpp files
+add the dispmap class
+add the dispface class
+get rid of patches.c or patches.cpp as the case may be
+
+DONE: create a parallel array for visited displacement faces
+change the visited array to a flag in the "bsp" ddispface_t structure
+DONE: add reset visited displacement face functionality
+DONE: visited neighbor flags
+DONE: reset visited neighbor flags
+DONE: move the radial_t structure and some of its functionality to a more global scope
+DONE: create a finallightdispface
+DONE: generate luxels for displacement face
+DONE: fix precomplightmapoffsets to take displacement faces into account
+fill in luxel data from sample data
+modify the GetPhongNormal and GatherSampleLight functions to use displacement face normals at "spot"
+make a lightinfo list? -- so we don't regenerate initlightinfo so many times?
diff --git a/mp/src/utils/vrad/origface.cpp b/mp/src/utils/vrad/origface.cpp new file mode 100644 index 00000000..43a19577 --- /dev/null +++ b/mp/src/utils/vrad/origface.cpp @@ -0,0 +1,51 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vrad.h"
+
+bool bOrigFacesTouched[MAX_MAP_FACES];
+
+
+//-----------------------------------------------------------------------------
+// Pupose: clear (reset) the bOrigFacesTouched list -- parellels the original
+// face list allowing an original face to only be processed once in
+// pairing edges!
+//-----------------------------------------------------------------------------
+void ResetOrigFacesTouched( void )
+{
+ for( int i = 0; i < MAX_MAP_FACES; i++ )
+ {
+ bOrigFacesTouched[i] = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: mark an original faces as touched (dirty)
+// Input: index - index of the original face touched
+//-----------------------------------------------------------------------------
+void SetOrigFaceTouched( int index )
+{
+ bOrigFacesTouched[index] = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: return whether or not an original face has been touched
+// Input: index - index of the original face touched
+// Output: true/false
+//-----------------------------------------------------------------------------
+bool IsOrigFaceTouched( int index )
+{
+ return bOrigFacesTouched[index];
+}
diff --git a/mp/src/utils/vrad/radial.cpp b/mp/src/utils/vrad/radial.cpp new file mode 100644 index 00000000..46e3b925 --- /dev/null +++ b/mp/src/utils/vrad/radial.cpp @@ -0,0 +1,882 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "lightmap.h"
+#include "radial.h"
+#include "mathlib/bumpvects.h"
+#include "utlrbtree.h"
+#include "mathlib/VMatrix.h"
+#include "macro_texture.h"
+
+
+void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord )
+{
+ Vector pos;
+
+ VectorSubtract( world, l->luxelOrigin, pos );
+ coord[0] = DotProduct( pos, l->worldToLuxelSpace[0] ) - l->face->m_LightmapTextureMinsInLuxels[0];
+ coord[1] = DotProduct( pos, l->worldToLuxelSpace[1] ) - l->face->m_LightmapTextureMinsInLuxels[1];
+}
+
+void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world )
+{
+ Vector pos;
+
+ s += l->face->m_LightmapTextureMinsInLuxels[0];
+ t += l->face->m_LightmapTextureMinsInLuxels[1];
+ VectorMA( l->luxelOrigin, s, l->luxelToWorldSpace[0], pos );
+ VectorMA( pos, t, l->luxelToWorldSpace[1], world );
+}
+
+void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord )
+{
+ FourVectors luxelOrigin;
+ luxelOrigin.DuplicateVector ( l->luxelOrigin );
+
+ FourVectors pos = world;
+ pos -= luxelOrigin;
+
+ coord.x = pos * l->worldToLuxelSpace[0];
+ coord.x = SubSIMD ( coord.x, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
+ coord.y = pos * l->worldToLuxelSpace[1];
+ coord.y = SubSIMD ( coord.y, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
+ coord.z = Four_Zeros;
+}
+
+void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world )
+{
+ world.DuplicateVector ( l->luxelOrigin );
+ FourVectors st;
+
+ s = AddSIMD ( s, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[0] ) );
+ st.DuplicateVector ( l->luxelToWorldSpace[0] );
+ st *= s;
+ world += st;
+
+ t = AddSIMD ( t, ReplicateX4 ( l->face->m_LightmapTextureMinsInLuxels[1] ) );
+ st.DuplicateVector ( l->luxelToWorldSpace[1] );
+ st *= t;
+ world += st;
+}
+
+
+
+void AddDirectToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ LightingValue_t const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap )
+{
+ int s_min, s_max, t_min, t_max;
+ Vector2D coord;
+ int s, t;
+ float ds, dt;
+ float r;
+ float area;
+ int bumpSample;
+
+ // convert world pos into local lightmap texture coord
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+
+ s_min = ( int )( coordmins[0] );
+ t_min = ( int )( coordmins[1] );
+ s_max = ( int )( coordmaxs[0] + 0.9999f ) + 1; // ????
+ t_max = ( int )( coordmaxs[1] + 0.9999f ) + 1;
+
+ s_min = max( s_min, 0 );
+ t_min = max( t_min, 0 );
+ s_max = min( s_max, rad->w );
+ t_max = min( t_max, rad->h );
+
+ for( s = s_min; s < s_max; s++ )
+ {
+ for( t = t_min; t < t_max; t++ )
+ {
+ float s0 = max( coordmins[0] - s, -1.0 );
+ float t0 = max( coordmins[1] - t, -1.0 );
+ float s1 = min( coordmaxs[0] - s, 1.0 );
+ float t1 = min( coordmaxs[1] - t, 1.0 );
+
+ area = (s1 - s0) * (t1 - t0);
+
+ if (area > EQUAL_EPSILON)
+ {
+ ds = fabs( coord[0] - s );
+ dt = fabs( coord[1] - t );
+
+ r = max( ds, dt );
+
+ if (r < 0.1)
+ {
+ r = area / 0.1;
+ }
+ else
+ {
+ r = area / r;
+ }
+
+ int i = s+t*rad->w;
+
+ if( hasBumpmap )
+ {
+ if( neighborHasBumpmap )
+ {
+ for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted(light[0],r );
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
+ }
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ }
+
+ rad->weight[i] += r;
+ }
+ }
+ }
+}
+
+
+
+void AddBouncedToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ Vector const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap )
+{
+ int s_min, s_max, t_min, t_max;
+ Vector2D coord;
+ int s, t;
+ float ds, dt;
+ float r;
+ int bumpSample;
+
+ // convert world pos into local lightmap texture coord
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+
+ float dists, distt;
+
+ dists = (coordmaxs[0] - coordmins[0]);
+ distt = (coordmaxs[1] - coordmins[1]);
+
+ // patches less than a luxel in size could be mistakeningly filtered, so clamp.
+ dists = max( 1.0, dists );
+ distt = max( 1.0, distt );
+
+ // find possible domain of patch influence
+ s_min = ( int )( coord[0] - dists * RADIALDIST );
+ t_min = ( int )( coord[1] - distt * RADIALDIST );
+ s_max = ( int )( coord[0] + dists * RADIALDIST + 1.0f );
+ t_max = ( int )( coord[1] + distt * RADIALDIST + 1.0f );
+
+ // clamp to valid luxel
+ s_min = max( s_min, 0 );
+ t_min = max( t_min, 0 );
+ s_max = min( s_max, rad->w );
+ t_max = min( t_max, rad->h );
+
+ for( s = s_min; s < s_max; s++ )
+ {
+ for( t = t_min; t < t_max; t++ )
+ {
+ // patch influence is based on patch size
+ ds = ( coord[0] - s ) / dists;
+ dt = ( coord[1] - t ) / distt;
+
+ r = RADIALDIST2 - (ds * ds + dt * dt);
+
+ int i = s+t*rad->w;
+
+ if (r > 0)
+ {
+ if( hasBumpmap )
+ {
+ if( neighborHasBumpmap )
+ {
+ for( bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[bumpSample], r );
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ for( bumpSample = 1; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ rad->light[bumpSample][i].AddWeighted( light[0], r * OO_SQRT_3 );
+ }
+ }
+ }
+ else
+ {
+ rad->light[0][i].AddWeighted( light[0], r );
+ }
+
+ rad->weight[i] += r;
+ }
+ }
+ }
+}
+
+void PatchLightmapCoordRange( radial_t *rad, int ndxPatch, Vector2D &mins, Vector2D &maxs )
+{
+ winding_t *w;
+ int i;
+ Vector2D coord;
+
+ mins.Init( 1E30, 1E30 );
+ maxs.Init( -1E30, -1E30 );
+
+ CPatch *patch = &g_Patches.Element( ndxPatch );
+ w = patch->winding;
+
+ for (i = 0; i < w->numpoints; i++)
+ {
+ WorldToLuxelSpace( &rad->l, w->p[i], coord );
+ mins[0] = min( mins[0], coord[0] );
+ maxs[0] = max( maxs[0], coord[0] );
+ mins[1] = min( mins[1], coord[1] );
+ maxs[1] = max( maxs[1], coord[1] );
+ }
+}
+
+radial_t *AllocateRadial( int facenum )
+{
+ radial_t *rad;
+
+ rad = ( radial_t* )calloc( 1, sizeof( *rad ) );
+
+ rad->facenum = facenum;
+ InitLightinfo( &rad->l, facenum );
+
+ rad->w = rad->l.face->m_LightmapTextureSizeInLuxels[0]+1;
+ rad->h = rad->l.face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ return rad;
+}
+
+void FreeRadial( radial_t *rad )
+{
+ if (rad)
+ free( rad );
+}
+
+
+radial_t *BuildPatchRadial( int facenum )
+{
+ int j;
+ radial_t *rad;
+ CPatch *patch;
+ faceneighbor_t *fn;
+ Vector2D mins, maxs;
+ bool needsBumpmap, neighborNeedsBumpmap;
+
+ needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ rad = AllocateRadial( facenum );
+
+ fn = &faceneighbor[ rad->facenum ];
+
+ CPatch *pNextPatch;
+
+ if( g_FacePatches.Element( rad->facenum ) != g_FacePatches.InvalidIndex() )
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( rad->facenum ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches with children
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // get the range of patch lightmap texture coords
+ int ndxPatch = patch - g_Patches.Base();
+ PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
+
+ if (patch->numtransfers == 0)
+ {
+ // Error, using patch that was never evaluated or has no samples
+ // patch->totallight[1] = 255;
+ }
+
+ //
+ // displacement surface patch origin position and normal vectors have been changed to
+ // represent the displacement surface position and normal -- for radial "blending"
+ // we need to get the base surface patch origin!
+ //
+ if( ValidDispFace( &g_pFaces[facenum] ) )
+ {
+ Vector patchOrigin;
+ WindingCenter (patch->winding, patchOrigin );
+ AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ else
+ {
+ AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ }
+ }
+
+ for (j=0 ; j<fn->numneighbors; j++)
+ {
+ if( g_FacePatches.Element( fn->neighbor[j] ) != g_FacePatches.InvalidIndex() )
+ {
+ for( patch = &g_Patches.Element( g_FacePatches.Element( fn->neighbor[j] ) ); patch; patch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNext );
+ }
+
+ // skip patches with children
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ // get the range of patch lightmap texture coords
+ int ndxPatch = patch - g_Patches.Base();
+ PatchLightmapCoordRange( rad, ndxPatch, mins, maxs );
+
+ neighborNeedsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ //
+ // displacement surface patch origin position and normal vectors have been changed to
+ // represent the displacement surface position and normal -- for radial "blending"
+ // we need to get the base surface patch origin!
+ //
+ if( ValidDispFace( &g_pFaces[fn->neighbor[j]] ) )
+ {
+ Vector patchOrigin;
+ WindingCenter (patch->winding, patchOrigin );
+ AddBouncedToRadial( rad, patchOrigin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ else
+ {
+ AddBouncedToRadial( rad, patch->origin, mins, maxs, patch->totallight.light,
+ needsBumpmap, needsBumpmap );
+ }
+ }
+ }
+ }
+
+ return rad;
+}
+
+
+radial_t *BuildLuxelRadial( int facenum, int style )
+{
+ LightingValue_t light[NUM_BUMP_VECTS + 1];
+
+ facelight_t *fl = &facelight[facenum];
+ faceneighbor_t *fn = &faceneighbor[facenum];
+
+ radial_t *rad = AllocateRadial( facenum );
+
+ bool needsBumpmap = texinfo[g_pFaces[facenum].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ for (int k=0 ; k<fl->numsamples ; k++)
+ {
+ if( needsBumpmap )
+ {
+ for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ light[bumpSample] = fl->light[style][bumpSample][k];
+ }
+ }
+ else
+ {
+ light[0] = fl->light[style][0][k];
+ }
+
+ AddDirectToRadial( rad, fl->sample[k].pos, fl->sample[k].mins, fl->sample[k].maxs, light, needsBumpmap, needsBumpmap );
+ }
+
+ for (int j = 0; j < fn->numneighbors; j++)
+ {
+ fl = &facelight[fn->neighbor[j]];
+
+ bool neighborHasBumpmap = false;
+
+ if( texinfo[g_pFaces[fn->neighbor[j]].texinfo].flags & SURF_BUMPLIGHT )
+ {
+ neighborHasBumpmap = true;
+ }
+
+ int nstyle = 0;
+
+ // look for style that matches
+ if (g_pFaces[fn->neighbor[j]].styles[nstyle] != g_pFaces[facenum].styles[style])
+ {
+ for (nstyle = 1; nstyle < MAXLIGHTMAPS; nstyle++ )
+ if ( g_pFaces[fn->neighbor[j]].styles[nstyle] == g_pFaces[facenum].styles[style] )
+ break;
+
+ // if not found, skip this neighbor
+ if (nstyle >= MAXLIGHTMAPS)
+ continue;
+ }
+
+ lightinfo_t l;
+
+ InitLightinfo( &l, fn->neighbor[j] );
+
+ for (int k=0 ; k<fl->numsamples ; k++)
+ {
+ if( neighborHasBumpmap )
+ {
+ for( int bumpSample = 0; bumpSample < NUM_BUMP_VECTS + 1; bumpSample++ )
+ {
+ light[bumpSample] = fl->light[nstyle][bumpSample][k];
+ }
+ }
+ else
+ {
+ light[0]=fl->light[nstyle][0][k];
+ }
+
+ Vector tmp;
+ Vector2D mins, maxs;
+
+ LuxelSpaceToWorld( &l, fl->sample[k].mins[0], fl->sample[k].mins[1], tmp );
+ WorldToLuxelSpace( &rad->l, tmp, mins );
+ LuxelSpaceToWorld( &l, fl->sample[k].maxs[0], fl->sample[k].maxs[1], tmp );
+ WorldToLuxelSpace( &rad->l, tmp, maxs );
+
+ AddDirectToRadial( rad, fl->sample[k].pos, mins, maxs, light,
+ needsBumpmap, neighborHasBumpmap );
+ }
+ }
+
+ return rad;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the closest light value for a given point on the surface
+// this is normally a 1:1 mapping
+//-----------------------------------------------------------------------------
+bool SampleRadial( radial_t *rad, Vector& pnt, LightingValue_t light[NUM_BUMP_VECTS + 1], int bumpSampleCount )
+{
+ int bumpSample;
+ Vector2D coord;
+
+ WorldToLuxelSpace( &rad->l, pnt, coord );
+ int u = ( int )( coord[0] + 0.5f );
+ int v = ( int )( coord[1] + 0.5f );
+ int i = u + v * rad->w;
+
+ if (u < 0 || u > rad->w || v < 0 || v > rad->h)
+ {
+ static bool warning = false;
+ if ( !warning )
+ {
+ // punting over to KenB
+ // 2d coord indexes off of lightmap, generation of pnt seems suspect
+ Warning( "SampleRadial: Punting, Waiting for fix\n" );
+ warning = true;
+ }
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
+ }
+ return false;
+ }
+
+ bool baseSampleOk = true;
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ light[bumpSample].Zero();
+
+ if (rad->weight[i] > WEIGHT_EPS)
+ {
+ light[bumpSample]= rad->light[bumpSample][i];
+ light[bumpSample].Scale( 1.0 / rad->weight[i] );
+ }
+ else
+ {
+ if ( bRed2Black )
+ {
+ // Error, luxel has no samples
+ light[bumpSample].m_vecLighting.Init( 0, 0, 0 );
+ }
+ else
+ {
+ // Error, luxel has no samples
+ // Yes, it actually should be 2550
+ light[bumpSample].m_vecLighting.Init( 2550, 0, 0 );
+ }
+
+ if (bumpSample == 0)
+ baseSampleOk = false;
+ }
+ }
+
+ return baseSampleOk;
+}
+
+bool FloatLess( float const& src1, float const& src2 )
+{
+ return src1 < src2;
+}
+
+
+//-----------------------------------------------------------------------------
+// Debugging!
+//-----------------------------------------------------------------------------
+void GetRandomColor( unsigned char *color )
+{
+ static bool firstTime = true;
+
+ if( firstTime )
+ {
+ firstTime = false;
+ srand( 0 );
+ }
+
+ color[0] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+ color[1] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+ color[2] = ( unsigned char )( rand() * ( 255.0f / VALVE_RAND_MAX ) );
+}
+
+
+#if 0
+// debugging! -- not accurate!
+void DumpLuxels( facelight_t *pFaceLight, Vector *luxelColors, int ndxFace )
+{
+ static FileHandle_t pFpLuxels = NULL;
+
+ ThreadLock();
+
+ if( !pFpLuxels )
+ {
+ pFpLuxels = g_pFileSystem->Open( "luxels.txt", "w" );
+ }
+
+ dface_t *pFace = &g_pFaces[ndxFace];
+ bool bDisp = ( pFace->dispinfo != -1 );
+
+ for( int ndx = 0; ndx < pFaceLight->numluxels; ndx++ )
+ {
+ WriteWinding( pFpLuxels, pFaceLight->sample[ndx].w, luxelColors[ndx] );
+ if( bDumpNormals && bDisp )
+ {
+ WriteNormal( pFpLuxels, pFaceLight->luxel[ndx], pFaceLight->luxelNormals[ndx], 15.0f, Vector( 255, 255, 0 ) );
+ }
+ }
+
+ ThreadUnlock();
+}
+#endif
+
+
+static FileHandle_t pFileLuxels[4] = { NULL, NULL, NULL, NULL };
+
+void DumpDispLuxels( int iFace, Vector &color, int iLuxel, int nBump )
+{
+ // Lock the thread and dump the luxel data.
+ ThreadLock();
+
+ // Get the face and facelight data.
+ facelight_t *pFaceLight = &facelight[iFace];
+
+ // Open the luxel files.
+ char szFileName[512];
+ for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
+ {
+ if ( pFileLuxels[iBump] == NULL )
+ {
+ sprintf( szFileName, "luxels_bump%d.txt", iBump );
+ pFileLuxels[iBump] = g_pFileSystem->Open( szFileName, "w" );
+ }
+ }
+
+ WriteWinding( pFileLuxels[nBump], pFaceLight->sample[iLuxel].w, color );
+
+ ThreadUnlock();
+}
+
+void CloseDispLuxels()
+{
+ for ( int iBump = 0; iBump < ( NUM_BUMP_VECTS+1 ); ++iBump )
+ {
+ if ( pFileLuxels[iBump] )
+ {
+ g_pFileSystem->Close( pFileLuxels[iBump] );
+ }
+ }
+}
+
+/*
+=============
+FinalLightFace
+
+Add the indirect lighting on top of the direct
+lighting and save into final map format
+=============
+*/
+void FinalLightFace( int iThread, int facenum )
+{
+ dface_t *f;
+ int i, j, k;
+ facelight_t *fl;
+ float minlight;
+ int lightstyles;
+ LightingValue_t lb[NUM_BUMP_VECTS + 1], v[NUM_BUMP_VECTS + 1];
+ unsigned char *pdata[NUM_BUMP_VECTS + 1];
+ int bumpSample;
+ radial_t *rad = NULL;
+ radial_t *prad = NULL;
+
+ f = &g_pFaces[facenum];
+
+ // test for non-lit texture
+ if ( texinfo[f->texinfo].flags & TEX_SPECIAL)
+ return;
+
+ fl = &facelight[facenum];
+
+
+ for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
+ {
+ if ( f->styles[lightstyles] == 255 )
+ break;
+ }
+ if ( !lightstyles )
+ return;
+
+
+ //
+ // sample the triangulation
+ //
+ minlight = FloatForKey (face_entity[facenum], "_minlight") * 128;
+
+ bool needsBumpmap = ( texinfo[f->texinfo].flags & SURF_BUMPLIGHT ) ? true : false;
+ int bumpSampleCount = needsBumpmap ? NUM_BUMP_VECTS + 1 : 1;
+
+ bool bDisp = ( f->dispinfo != -1 );
+
+//#define RANDOM_COLOR
+
+#ifdef RANDOM_COLOR
+ unsigned char randomColor[3];
+ GetRandomColor( randomColor );
+#endif
+
+
+ // NOTE: I'm using these RB trees to sort all the illumination values
+ // to compute median colors. Turns out that this is a somewhat better
+ // method that using the average; usually if there are surfaces
+ // with a large light intensity variation, the extremely bright regions
+ // have a very small area and tend to influence the average too much.
+ CUtlRBTree< float, int > m_Red( 0, 256, FloatLess );
+ CUtlRBTree< float, int > m_Green( 0, 256, FloatLess );
+ CUtlRBTree< float, int > m_Blue( 0, 256, FloatLess );
+
+ for (k=0 ; k < lightstyles; k++ )
+ {
+ m_Red.RemoveAll();
+ m_Green.RemoveAll();
+ m_Blue.RemoveAll();
+
+ if (!do_fast)
+ {
+ if( !bDisp )
+ {
+ rad = BuildLuxelRadial( facenum, k );
+ }
+ else
+ {
+ rad = StaticDispMgr()->BuildLuxelRadial( facenum, k, needsBumpmap );
+ }
+ }
+
+ if (numbounce > 0 && k == 0)
+ {
+ // currently only radiosity light non-displacement surfaces!
+ if( !bDisp )
+ {
+ prad = BuildPatchRadial( facenum );
+ }
+ else
+ {
+ prad = StaticDispMgr()->BuildPatchRadial( facenum, needsBumpmap );
+ }
+ }
+
+ // pack the nonbump texture and the three bump texture for the given
+ // lightstyle right next to each other.
+ // NOTE: Even though it's building positions for all bump-mapped data,
+ // it isn't going to use those positions (see loop over bumpSample below)
+ // The file offset is correctly computed to only store space for 1 set
+ // of light data if we don't have bumped lighting.
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ pdata[bumpSample] = &(*pdlightdata)[f->lightofs + (k * bumpSampleCount + bumpSample) * fl->numluxels*4];
+ }
+
+ // Compute the average luxel color, but not for the bump samples
+ Vector avg( 0.0f, 0.0f, 0.0f );
+ int avgCount = 0;
+
+ for (j=0 ; j<fl->numluxels; j++)
+ {
+ // garymct - direct lighting
+ bool baseSampleOk = true;
+
+ if (!do_fast)
+ {
+ if( !bDisp )
+ {
+ baseSampleOk = SampleRadial( rad, fl->luxel[j], lb, bumpSampleCount );
+ }
+ else
+ {
+ baseSampleOk = StaticDispMgr()->SampleRadial( facenum, rad, fl->luxel[j], j, lb, bumpSampleCount, false );
+ }
+ }
+ else
+ {
+ for ( int iBump = 0 ; iBump < bumpSampleCount; iBump++ )
+ {
+ lb[iBump] = fl->light[0][iBump][j];
+ }
+ }
+
+ if (prad)
+ {
+ // garymct - bounced light
+ // v is indirect light that is received on the luxel.
+ if( !bDisp )
+ {
+ SampleRadial( prad, fl->luxel[j], v, bumpSampleCount );
+ }
+ else
+ {
+ StaticDispMgr()->SampleRadial( facenum, prad, fl->luxel[j], j, v, bumpSampleCount, true );
+ }
+
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ lb[bumpSample].AddLight( v[bumpSample] );
+ }
+ }
+
+ if ( bDisp && g_bDumpPatches )
+ {
+ for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample )
+ {
+ DumpDispLuxels( facenum, lb[bumpSample].m_vecLighting, j, bumpSample );
+ }
+ }
+
+ if (fl->numsamples == 0)
+ {
+ for( i = 0; i < bumpSampleCount; i++ )
+ {
+ lb[i].Init( 255, 0, 0 );
+ }
+ baseSampleOk = false;
+ }
+
+ int bumpSample;
+ for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ )
+ {
+ // clip from the bottom first
+ // garymct: minlight is a per entity minimum light value?
+ for( i=0; i<3; i++ )
+ {
+ lb[bumpSample].m_vecLighting[i] = max( lb[bumpSample].m_vecLighting[i], minlight );
+ }
+
+ // Do the average light computation, I'm assuming (perhaps incorrectly?)
+ // that all luxels in a particular lightmap have the same area here.
+ // Also, don't bother doing averages for the bump samples. Doing it here
+ // because of the minlight clamp above + the random color testy thingy.
+ // Also have to do it before Vec3toColorRGBExp32 because it
+ // destructively modifies lb[bumpSample] (Feh!)
+ if ((bumpSample == 0) && baseSampleOk)
+ {
+ ++avgCount;
+
+ ApplyMacroTextures( facenum, fl->luxel[j], lb[0].m_vecLighting );
+
+ // For median computation
+ m_Red.Insert( lb[bumpSample].m_vecLighting[0] );
+ m_Green.Insert( lb[bumpSample].m_vecLighting[1] );
+ m_Blue.Insert( lb[bumpSample].m_vecLighting[2] );
+ }
+
+#ifdef RANDOM_COLOR
+ pdata[bumpSample][0] = randomColor[0] / ( bumpSample + 1 );
+ pdata[bumpSample][1] = randomColor[1] / ( bumpSample + 1 );
+ pdata[bumpSample][2] = randomColor[2] / ( bumpSample + 1 );
+ pdata[bumpSample][3] = 0;
+#else
+ // convert to a 4 byte r,g,b,signed exponent format
+ VectorToColorRGBExp32( Vector( lb[bumpSample].m_vecLighting.x, lb[bumpSample].m_vecLighting.y,
+ lb[bumpSample].m_vecLighting.z ), *( ColorRGBExp32 *)pdata[bumpSample] );
+#endif
+
+ pdata[bumpSample] += 4;
+ }
+ }
+ FreeRadial( rad );
+ if (prad)
+ {
+ FreeRadial( prad );
+ prad = NULL;
+ }
+
+ // Compute the median color for this lightstyle
+ // Remember, the data goes *before* the specified light_ofs, in *reverse order*
+ ColorRGBExp32 *pAvgColor = dface_AvgLightColor( f, k );
+ if (avgCount == 0)
+ {
+ Vector median( 0, 0, 0 );
+ VectorToColorRGBExp32( median, *pAvgColor );
+ }
+ else
+ {
+ unsigned int r, g, b;
+ r = m_Red.FirstInorder();
+ g = m_Green.FirstInorder();
+ b = m_Blue.FirstInorder();
+ avgCount >>= 1;
+ while (avgCount > 0)
+ {
+ r = m_Red.NextInorder(r);
+ g = m_Green.NextInorder(g);
+ b = m_Blue.NextInorder(b);
+ --avgCount;
+ }
+
+ Vector median( m_Red[r], m_Green[g], m_Blue[b] );
+ VectorToColorRGBExp32( median, *pAvgColor );
+ }
+ }
+}
diff --git a/mp/src/utils/vrad/radial.h b/mp/src/utils/vrad/radial.h new file mode 100644 index 00000000..862167cb --- /dev/null +++ b/mp/src/utils/vrad/radial.h @@ -0,0 +1,76 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef RADIAL_H
+#define RADIAL_H
+#pragma once
+
+#include "mathlib/bumpvects.h"
+#include "mathlib/ssemath.h"
+#include "lightmap.h"
+
+#define RADIALDIST2 2 // (1.25*1.25+1.25*1.25)
+#define RADIALDIST 1.42 // 1.77 // sqrt( RADIALDIST2 )
+
+#define WEIGHT_EPS 0.00001f
+
+//-----------------------------------------------------------------------------
+// The radial_t data structure is used to accumulate irregularly spaced and irregularly
+// shaped direct and indirect lighting samples into a uniformly spaced and shaped luxel grid.
+//
+// The name "radial" is more historical than discriptive; it stems from the filtering method,
+// one of several methods initially tried. Since all the other methods have since been deleted,
+// it would probably be more accurate to rename it something like "LuxelAccumulationBucket" or
+// something similar, but since "radial" is fairly meaningless it's not like it's actually confusing
+// the issue.
+//-----------------------------------------------------------------------------
+typedef struct radial_s
+{
+ int facenum;
+ lightinfo_t l;
+ int w, h;
+ float weight[SINGLEMAP];
+ LightingValue_t light[NUM_BUMP_VECTS + 1][SINGLEMAP];
+} radial_t;
+
+
+void WorldToLuxelSpace( lightinfo_t const *l, Vector const &world, Vector2D &coord );
+void LuxelSpaceToWorld( lightinfo_t const *l, float s, float t, Vector &world );
+
+void WorldToLuxelSpace( lightinfo_t const *l, FourVectors const &world, FourVectors &coord );
+void LuxelSpaceToWorld( lightinfo_t const *l, fltx4 s, fltx4 t, FourVectors &world );
+
+void AddDirectToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ Vector const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap );
+
+void AddBounceToRadial( radial_t *rad,
+ Vector const &pnt,
+ Vector2D const &coordmins, Vector2D const &coordmaxs,
+ Vector const light[NUM_BUMP_VECTS+1],
+ bool hasBumpmap, bool neighborHasBumpmap );
+
+bool SampleRadial( radial_t *rad, Vector& pnt, Vector light[NUM_BUMP_VECTS+1], int bumpSampleCount );
+
+radial_t *AllocateRadial( int facenum );
+void FreeRadial( radial_t *rad );
+
+bool SampleRadial( radial_t *rad, Vector& pnt, Vector light[NUM_BUMP_VECTS + 1], int bumpSampleCount );
+radial_t *BuildPatchRadial( int facenum );
+
+// utilities
+bool FloatLess( float const& src1, float const& src2 );
+
+#endif
diff --git a/mp/src/utils/vrad/samplehash.cpp b/mp/src/utils/vrad/samplehash.cpp new file mode 100644 index 00000000..fb35d351 --- /dev/null +++ b/mp/src/utils/vrad/samplehash.cpp @@ -0,0 +1,230 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "lightmap.h"
+
+#define SAMPLEHASH_NUM_BUCKETS 65536
+#define SAMPLEHASH_GROW_SIZE 0
+#define SAMPLEHASH_INIT_SIZE 0
+
+int samplesAdded = 0;
+int patchSamplesAdded = 0;
+static unsigned short g_PatchIterationKey = 0;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool SampleData_CompareFunc( SampleData_t const &src1, SampleData_t const &src2 )
+{
+ return ( ( src1.x == src2.x ) &&
+ ( src1.y == src2.y ) &&
+ ( src1.z == src2.z ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+unsigned int SampleData_KeyFunc( SampleData_t const &src )
+{
+ return ( src.x + src.y + src.z );
+}
+
+
+CUtlHash<SampleData_t> g_SampleHashTable( SAMPLEHASH_NUM_BUCKETS,
+ SAMPLEHASH_GROW_SIZE,
+ SAMPLEHASH_INIT_SIZE,
+ SampleData_CompareFunc, SampleData_KeyFunc );
+
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+UtlHashHandle_t SampleData_Find( sample_t *pSample )
+{
+ SampleData_t sampleData;
+ sampleData.x = ( int )( pSample->pos.x / SAMPLEHASH_VOXEL_SIZE ) * 100;
+ sampleData.y = ( int )( pSample->pos.y / SAMPLEHASH_VOXEL_SIZE ) * 10;
+ sampleData.z = ( int )( pSample->pos.z / SAMPLEHASH_VOXEL_SIZE );
+
+ return g_SampleHashTable.Find( sampleData );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+UtlHashHandle_t SampleData_InsertIntoHashTable( sample_t *pSample, SampleHandle_t sampleHandle )
+{
+ SampleData_t sampleData;
+ sampleData.x = ( int )( pSample->pos.x / SAMPLEHASH_VOXEL_SIZE ) * 100;
+ sampleData.y = ( int )( pSample->pos.y / SAMPLEHASH_VOXEL_SIZE ) * 10;
+ sampleData.z = ( int )( pSample->pos.z / SAMPLEHASH_VOXEL_SIZE );
+
+ UtlHashHandle_t handle = g_SampleHashTable.AllocEntryFromKey( sampleData );
+
+ SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
+ pSampleData->x = sampleData.x;
+ pSampleData->y = sampleData.y;
+ pSampleData->z = sampleData.z;
+ pSampleData->m_Samples.AddToTail( sampleHandle );
+
+ samplesAdded++;
+
+ return handle;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+UtlHashHandle_t SampleData_AddSample( sample_t *pSample, SampleHandle_t sampleHandle )
+{
+
+ // find the key -- if it doesn't exist add new sample data to the
+ // hash table
+ UtlHashHandle_t handle = SampleData_Find( pSample );
+ if( handle == g_SampleHashTable.InvalidHandle() )
+ {
+ handle = SampleData_InsertIntoHashTable( pSample, sampleHandle );
+ }
+ else
+ {
+ SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
+ pSampleData->m_Samples.AddToTail( sampleHandle );
+
+ samplesAdded++;
+ }
+
+ return handle;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void SampleData_Log( void )
+{
+ if( g_bLogHashData )
+ {
+ g_SampleHashTable.Log( "samplehash.txt" );
+ }
+}
+
+
+//=============================================================================
+//=============================================================================
+//
+// PatchSample Functions
+//
+//=============================================================================
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool PatchSampleData_CompareFunc( PatchSampleData_t const &src1, PatchSampleData_t const &src2 )
+{
+ return ( ( src1.x == src2.x ) &&
+ ( src1.y == src2.y ) &&
+ ( src1.z == src2.z ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+unsigned int PatchSampleData_KeyFunc( PatchSampleData_t const &src )
+{
+ return ( src.x + src.y + src.z );
+}
+
+
+CUtlHash<PatchSampleData_t> g_PatchSampleHashTable( SAMPLEHASH_NUM_BUCKETS,
+ SAMPLEHASH_GROW_SIZE,
+ SAMPLEHASH_INIT_SIZE,
+ PatchSampleData_CompareFunc, PatchSampleData_KeyFunc );
+
+void GetPatchSampleHashXYZ( const Vector &vOrigin, int &x, int &y, int &z )
+{
+ x = ( int )( vOrigin.x / SAMPLEHASH_VOXEL_SIZE );
+ y = ( int )( vOrigin.y / SAMPLEHASH_VOXEL_SIZE );
+ z = ( int )( vOrigin.z / SAMPLEHASH_VOXEL_SIZE );
+}
+
+
+unsigned short IncrementPatchIterationKey()
+{
+ if ( g_PatchIterationKey == 0xFFFF )
+ {
+ g_PatchIterationKey = 1;
+ for ( int i=0; i < g_Patches.Count(); i++ )
+ g_Patches[i].m_IterationKey = 0;
+ }
+ else
+ {
+ g_PatchIterationKey++;
+ }
+ return g_PatchIterationKey;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void PatchSampleData_AddSample( CPatch *pPatch, int ndxPatch )
+{
+ int patchSampleMins[3], patchSampleMaxs[3];
+
+#if defined( SAMPLEHASH_USE_AREA_PATCHES )
+ GetPatchSampleHashXYZ( pPatch->mins, patchSampleMins[0], patchSampleMins[1], patchSampleMins[2] );
+ GetPatchSampleHashXYZ( pPatch->maxs, patchSampleMaxs[0], patchSampleMaxs[1], patchSampleMaxs[2] );
+#else
+ // If not using area patches, just use the patch's origin to add it to the voxels.
+ GetPatchSampleHashXYZ( pPatch->origin, patchSampleMins[0], patchSampleMins[1], patchSampleMins[2] );
+ memcpy( patchSampleMaxs, patchSampleMins, sizeof( patchSampleMaxs ) );
+#endif
+
+ // Make sure mins are smaller than maxs so we don't iterate for 4 bil.
+ Assert( patchSampleMins[0] <= patchSampleMaxs[0] && patchSampleMins[1] <= patchSampleMaxs[1] && patchSampleMins[2] <= patchSampleMaxs[2] );
+ patchSampleMins[0] = min( patchSampleMins[0], patchSampleMaxs[0] );
+ patchSampleMins[1] = min( patchSampleMins[1], patchSampleMaxs[1] );
+ patchSampleMins[2] = min( patchSampleMins[2], patchSampleMaxs[2] );
+
+ int iterateCoords[3];
+ for ( iterateCoords[0]=patchSampleMins[0]; iterateCoords[0] <= patchSampleMaxs[0]; iterateCoords[0]++ )
+ {
+ for ( iterateCoords[1]=patchSampleMins[1]; iterateCoords[1] <= patchSampleMaxs[1]; iterateCoords[1]++ )
+ {
+ for ( iterateCoords[2]=patchSampleMins[2]; iterateCoords[2] <= patchSampleMaxs[2]; iterateCoords[2]++ )
+ {
+ // find the key -- if it doesn't exist add new sample data to the
+ // hash table
+ PatchSampleData_t iteratePatch;
+ iteratePatch.x = iterateCoords[0] * 100;
+ iteratePatch.y = iterateCoords[1] * 10;
+ iteratePatch.z = iterateCoords[2];
+
+ UtlHashHandle_t handle = g_PatchSampleHashTable.Find( iteratePatch );
+ if( handle == g_PatchSampleHashTable.InvalidHandle() )
+ {
+ UtlHashHandle_t handle = g_PatchSampleHashTable.AllocEntryFromKey( iteratePatch );
+
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+ pPatchData->x = iteratePatch.x;
+ pPatchData->y = iteratePatch.y;
+ pPatchData->z = iteratePatch.z;
+ pPatchData->m_ndxPatches.AddToTail( ndxPatch );
+
+ patchSamplesAdded++;
+ }
+ else
+ {
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+ pPatchData->m_ndxPatches.AddToTail( ndxPatch );
+
+ patchSamplesAdded++;
+ }
+ }
+ }
+ }
+}
+
diff --git a/mp/src/utils/vrad/trace.cpp b/mp/src/utils/vrad/trace.cpp new file mode 100644 index 00000000..f049fc40 --- /dev/null +++ b/mp/src/utils/vrad/trace.cpp @@ -0,0 +1,644 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+// trace.c
+
+//=============================================================================
+
+#include "vrad.h"
+#include "trace.h"
+#include "Cmodel.h"
+#include "mathlib/vmatrix.h"
+
+
+//=============================================================================
+
+class CToolTrace : public CBaseTrace
+{
+public:
+ CToolTrace() {}
+
+ Vector mins;
+ Vector maxs;
+ Vector extents;
+
+ texinfo_t *surface;
+
+ qboolean ispoint;
+
+private:
+ CToolTrace( const CToolTrace& );
+};
+
+
+// 1/32 epsilon to keep floating point happy
+#define DIST_EPSILON (0.03125)
+
+// JAYHL2: This used to be -1, but that caused lots of epsilon issues
+// around slow sloping planes. Perhaps Quake2 limited maps to a certain
+// slope / angle on walkable ground. It has to be a negative number
+// so that the tests work out.
+#define NEVER_UPDATED -9999
+
+//=============================================================================
+
+bool DM_RayDispIntersectTest( CVRADDispColl *pTree, Vector& rayStart, Vector& rayEnd, CToolTrace *pTrace );
+void DM_ClipBoxToBrush( CToolTrace *trace, const Vector & mins, const Vector & maxs, const Vector& p1, const Vector& p2, dbrush_t *brush );
+
+//=============================================================================
+
+float TraceLeafBrushes( int leafIndex, const Vector &start, const Vector &end, CBaseTrace &traceOut )
+{
+ dleaf_t *pLeaf = dleafs + leafIndex;
+ CToolTrace trace;
+ memset( &trace, 0, sizeof(trace) );
+ trace.ispoint = true;
+ trace.startsolid = false;
+ trace.fraction = 1.0;
+
+ for ( int i = 0; i < pLeaf->numleafbrushes; i++ )
+ {
+ int brushnum = dleafbrushes[pLeaf->firstleafbrush+i];
+ dbrush_t *b = &dbrushes[brushnum];
+ if ( !(b->contents & MASK_OPAQUE))
+ continue;
+
+ Vector zeroExtents = vec3_origin;
+ DM_ClipBoxToBrush( &trace, zeroExtents, zeroExtents, start, end, b);
+ if ( trace.fraction != 1.0 || trace.startsolid )
+ {
+ if ( trace.startsolid )
+ trace.fraction = 0.0f;
+ traceOut = trace;
+ return trace.fraction;
+ }
+ }
+ traceOut = trace;
+ return 1.0f;
+}
+
+DispTested_t s_DispTested[MAX_TOOL_THREADS+1];
+
+// this just uses the average coverage for the triangle
+class CCoverageCount : public ITransparentTriangleCallback
+{
+public:
+ CCoverageCount()
+ {
+ m_coverage = Four_Zeros;
+ }
+
+ virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
+ {
+ float color = g_RtEnv.GetTriangleColor( hitID ).x;
+ m_coverage = AddSIMD( m_coverage, AndSIMD ( *pHitMask, ReplicateX4 ( color ) ) );
+ m_coverage = MinSIMD( m_coverage, Four_Ones );
+
+ fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
+
+ // we should continue if the ones that hit the triangle have onesMask set to zero
+ // so hitMask & onesMask != hitMask
+ // so hitMask & onesMask == hitMask means we're done
+ // so ts(hitMask & onesMask == hitMask) != 0xF says go on
+ return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
+ }
+
+ fltx4 GetCoverage()
+ {
+ return m_coverage;
+ }
+
+ fltx4 GetFractionVisible()
+ {
+ return SubSIMD ( Four_Ones, m_coverage );
+ }
+
+ fltx4 m_coverage;
+};
+
+// this will sample the texture to get a coverage at the ray intersection point
+class CCoverageCountTexture : public CCoverageCount
+{
+public:
+ virtual bool VisitTriangle_ShouldContinue( const TriIntersectData_t &triangle, const FourRays &rays, fltx4 *pHitMask, fltx4 *b0, fltx4 *b1, fltx4 *b2, int32 hitID )
+ {
+ int sign = TestSignSIMD( *pHitMask );
+ float addedCoverage[4];
+ for ( int s = 0; s < 4; s++)
+ {
+ addedCoverage[s] = 0.0f;
+ if ( ( sign >> s) & 0x1 )
+ {
+ addedCoverage[s] = ComputeCoverageFromTexture( b0->m128_f32[s], b1->m128_f32[s], b2->m128_f32[s], hitID );
+ }
+ }
+ m_coverage = AddSIMD( m_coverage, LoadUnalignedSIMD( addedCoverage ) );
+ m_coverage = MinSIMD( m_coverage, Four_Ones );
+ fltx4 onesMask = CmpEqSIMD( m_coverage, Four_Ones );
+
+ // we should continue if the ones that hit the triangle have onesMask set to zero
+ // so hitMask & onesMask != hitMask
+ // so hitMask & onesMask == hitMask means we're done
+ // so ts(hitMask & onesMask == hitMask) != 0xF says go on
+ return 0xF != TestSignSIMD ( CmpEqSIMD ( AndSIMD( *pHitMask, onesMask ), *pHitMask ) );
+ }
+};
+
+void TestLine( const FourVectors& start, const FourVectors& stop,
+ fltx4 *pFractionVisible, int static_prop_index_to_ignore )
+{
+ FourRays myrays;
+ myrays.origin = start;
+ myrays.direction = stop;
+ myrays.direction -= myrays.origin;
+ fltx4 len = myrays.direction.length();
+ myrays.direction *= ReciprocalSIMD( len );
+
+ RayTracingResult rt_result;
+ CCoverageCountTexture coverageCallback;
+
+ g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_index_to_ignore, g_bTextureShadows ? &coverageCallback : 0 );
+
+ // Assume we can see the targets unless we get hits
+ float visibility[4];
+ for ( int i = 0; i < 4; i++ )
+ {
+ visibility[i] = 1.0f;
+ if ( ( rt_result.HitIds[i] != -1 ) &&
+ ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
+ {
+ visibility[i] = 0.0f;
+ }
+ }
+ *pFractionVisible = LoadUnalignedSIMD( visibility );
+ if ( g_bTextureShadows )
+ *pFractionVisible = MinSIMD( *pFractionVisible, coverageCallback.GetFractionVisible() );
+}
+
+
+
+/*
+================
+DM_ClipBoxToBrush
+================
+*/
+void DM_ClipBoxToBrush( CToolTrace *trace, const Vector& mins, const Vector& maxs, const Vector& p1, const Vector& p2,
+ dbrush_t *brush)
+{
+ dplane_t *plane, *clipplane;
+ float dist;
+ Vector ofs;
+ float d1, d2;
+ float f;
+ dbrushside_t *side, *leadside;
+
+ if (!brush->numsides)
+ return;
+
+ float enterfrac = NEVER_UPDATED;
+ float leavefrac = 1.f;
+ clipplane = NULL;
+
+ bool getout = false;
+ bool startout = false;
+ leadside = NULL;
+
+ // Loop interchanged, so we don't have to check trace->ispoint every side.
+ if ( !trace->ispoint )
+ {
+ for (int i=0 ; i<brush->numsides ; ++i)
+ {
+ side = &dbrushsides[brush->firstside+i];
+ plane = dplanes + side->planenum;
+
+ // FIXME: special case for axial
+
+ // general box case
+ // push the plane out apropriately for mins/maxs
+
+ // FIXME: use signbits into 8 way lookup for each mins/maxs
+ ofs.x = (plane->normal.x < 0) ? maxs.x : mins.x;
+ ofs.y = (plane->normal.y < 0) ? maxs.y : mins.y;
+ ofs.z = (plane->normal.z < 0) ? maxs.z : mins.z;
+// for (j=0 ; j<3 ; j++)
+// {
+ // Set signmask to either 0 if the sign is negative, or 0xFFFFFFFF is the sign is positive:
+ //int signmask = (((*(int *)&(plane->normal[j]))&0x80000000) >> 31) - 1;
+
+ //float temp = maxs[j];
+ //*(int *)&(ofs[j]) = (~signmask) & (*(int *)&temp);
+ //float temp1 = mins[j];
+ //*(int *)&(ofs[j]) |= (signmask) & (*(int *)&temp1);
+// }
+ dist = DotProduct (ofs, plane->normal);
+ dist = plane->dist - dist;
+
+ d1 = DotProduct (p1, plane->normal) - dist;
+ d2 = DotProduct (p2, plane->normal) - dist;
+
+ // if completely in front of face, no intersection
+ if (d1 > 0 && d2 > 0)
+ return;
+
+ if (d2 > 0)
+ getout = true; // endpoint is not in solid
+ if (d1 > 0)
+ startout = true;
+
+ if (d1 <= 0 && d2 <= 0)
+ continue;
+
+ // crosses face
+ if (d1 > d2)
+ { // enter
+ f = (d1-DIST_EPSILON) / (d1-d2);
+ if (f > enterfrac)
+ {
+ enterfrac = f;
+ clipplane = plane;
+ leadside = side;
+ }
+ }
+ else
+ { // leave
+ f = (d1+DIST_EPSILON) / (d1-d2);
+ if (f < leavefrac)
+ leavefrac = f;
+ }
+ }
+ }
+ else
+ {
+ for (int i=0 ; i<brush->numsides ; ++i)
+ {
+ side = &dbrushsides[brush->firstside+i];
+ plane = dplanes + side->planenum;
+
+ // FIXME: special case for axial
+
+ // special point case
+ // don't ray trace against bevel planes
+ if( side->bevel == 1 )
+ continue;
+
+ dist = plane->dist;
+ d1 = DotProduct (p1, plane->normal) - dist;
+ d2 = DotProduct (p2, plane->normal) - dist;
+
+ // if completely in front of face, no intersection
+ if (d1 > 0 && d2 > 0)
+ return;
+
+ if (d2 > 0)
+ getout = true; // endpoint is not in solid
+ if (d1 > 0)
+ startout = true;
+
+ if (d1 <= 0 && d2 <= 0)
+ continue;
+
+ // crosses face
+ if (d1 > d2)
+ { // enter
+ f = (d1-DIST_EPSILON) / (d1-d2);
+ if (f > enterfrac)
+ {
+ enterfrac = f;
+ clipplane = plane;
+ leadside = side;
+ }
+ }
+ else
+ { // leave
+ f = (d1+DIST_EPSILON) / (d1-d2);
+ if (f < leavefrac)
+ leavefrac = f;
+ }
+ }
+ }
+
+
+
+ if (!startout)
+ { // original point was inside brush
+ trace->startsolid = true;
+ if (!getout)
+ trace->allsolid = true;
+ return;
+ }
+ if (enterfrac < leavefrac)
+ {
+ if (enterfrac > NEVER_UPDATED && enterfrac < trace->fraction)
+ {
+ if (enterfrac < 0)
+ enterfrac = 0;
+ trace->fraction = enterfrac;
+ trace->plane.dist = clipplane->dist;
+ trace->plane.normal = clipplane->normal;
+ trace->plane.type = clipplane->type;
+ if (leadside->texinfo!=-1)
+ trace->surface = &texinfo[leadside->texinfo];
+ else
+ trace->surface = 0;
+ trace->contents = brush->contents;
+ }
+ }
+}
+
+void TestLine_DoesHitSky( FourVectors const& start, FourVectors const& stop,
+ fltx4 *pFractionVisible, bool canRecurse, int static_prop_to_skip, bool bDoDebug )
+{
+ FourRays myrays;
+ myrays.origin = start;
+ myrays.direction = stop;
+ myrays.direction -= myrays.origin;
+ fltx4 len = myrays.direction.length();
+ myrays.direction *= ReciprocalSIMD( len );
+ RayTracingResult rt_result;
+ CCoverageCountTexture coverageCallback;
+
+ g_RtEnv.Trace4Rays(myrays, Four_Zeros, len, &rt_result, TRACE_ID_STATICPROP | static_prop_to_skip, g_bTextureShadows? &coverageCallback : 0);
+
+ if ( bDoDebug )
+ {
+ WriteTrace( "trace.txt", myrays, rt_result );
+ }
+
+ float aOcclusion[4];
+ for ( int i = 0; i < 4; i++ )
+ {
+ aOcclusion[i] = 0.0f;
+ if ( ( rt_result.HitIds[i] != -1 ) &&
+ ( rt_result.HitDistance.m128_f32[i] < len.m128_f32[i] ) )
+ {
+ int id = g_RtEnv.OptimizedTriangleList[rt_result.HitIds[i]].m_Data.m_IntersectData.m_nTriangleID;
+ if ( !( id & TRACE_ID_SKY ) )
+ aOcclusion[i] = 1.0f;
+ }
+ }
+ fltx4 occlusion = LoadUnalignedSIMD( aOcclusion );
+ if (g_bTextureShadows)
+ occlusion = MaxSIMD ( occlusion, coverageCallback.GetCoverage() );
+
+ bool fullyOccluded = ( TestSignSIMD( CmpGeSIMD( occlusion, Four_Ones ) ) == 0xF );
+
+ // if we hit sky, and we're not in a sky camera's area, try clipping into the 3D sky boxes
+ if ( (! fullyOccluded) && canRecurse && (! g_bNoSkyRecurse ) )
+ {
+ FourVectors dir = stop;
+ dir -= start;
+ dir.VectorNormalize();
+
+ int leafIndex = -1;
+ leafIndex = PointLeafnum( start.Vec( 0 ) );
+ if ( leafIndex >= 0 )
+ {
+ int area = dleafs[leafIndex].area;
+ if (area >= 0 && area < numareas)
+ {
+ if (area_sky_cameras[area] < 0)
+ {
+ int cam;
+ for (cam = 0; cam < num_sky_cameras; ++cam)
+ {
+ FourVectors skystart, skytrans, skystop;
+ skystart.DuplicateVector( sky_cameras[cam].origin );
+ skystop = start;
+ skystop *= sky_cameras[cam].world_to_sky;
+ skystart += skystop;
+
+ skystop = dir;
+ skystop *= MAX_TRACE_LENGTH;
+ skystop += skystart;
+ TestLine_DoesHitSky ( skystart, skystop, pFractionVisible, false, static_prop_to_skip, bDoDebug );
+ occlusion = AddSIMD ( occlusion, Four_Ones );
+ occlusion = SubSIMD ( occlusion, *pFractionVisible );
+ }
+ }
+ }
+ }
+ }
+
+ occlusion = MaxSIMD( occlusion, Four_Zeros );
+ occlusion = MinSIMD( occlusion, Four_Ones );
+ *pFractionVisible = SubSIMD( Four_Ones, occlusion );
+}
+
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int PointLeafnum_r( const Vector &point, int ndxNode )
+{
+ // while loop here is to avoid recursion overhead
+ while( ndxNode >= 0 )
+ {
+ dnode_t *pNode = dnodes + ndxNode;
+ dplane_t *pPlane = dplanes + pNode->planenum;
+
+ float dist;
+ if( pPlane->type < 3 )
+ {
+ dist = point[pPlane->type] - pPlane->dist;
+ }
+ else
+ {
+ dist = DotProduct( pPlane->normal, point ) - pPlane->dist;
+ }
+
+ if( dist < 0.0f )
+ {
+ ndxNode = pNode->children[1];
+ }
+ else
+ {
+ ndxNode = pNode->children[0];
+ }
+ }
+
+ return ( -1 - ndxNode );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int PointLeafnum( const Vector &point )
+{
+ return PointLeafnum_r( point, 0 );
+}
+
+// this iterates the list of entities looking for _vradshadows 1
+// each brush entity containing this key is added to the raytracing environment
+// as a triangle soup model.
+
+dmodel_t *BrushmodelForEntity( entity_t *pEntity )
+{
+ const char *pModelname = ValueForKey( pEntity, "model" );
+ if ( Q_strlen(pModelname) > 1 )
+ {
+ int modelIndex = atol( pModelname + 1 );
+ if ( modelIndex > 0 && modelIndex < nummodels )
+ {
+ return &dmodels[modelIndex];
+ }
+ }
+ return NULL;
+}
+
+void AddBrushToRaytraceEnvironment( dbrush_t *pBrush, const VMatrix &xform )
+{
+ if ( !( pBrush->contents & MASK_OPAQUE ) )
+ return;
+
+ Vector v0, v1, v2;
+ for (int i = 0; i < pBrush->numsides; i++ )
+ {
+ dbrushside_t *side = &dbrushsides[pBrush->firstside + i];
+ dplane_t *plane = &dplanes[side->planenum];
+ texinfo_t *tx = &texinfo[side->texinfo];
+ winding_t *w = BaseWindingForPlane (plane->normal, plane->dist);
+
+ if ( tx->flags & SURF_SKY || side->dispinfo )
+ continue;
+
+ for (int j=0 ; j<pBrush->numsides && w; j++)
+ {
+ if (i == j)
+ continue;
+ dbrushside_t *pOtherSide = &dbrushsides[pBrush->firstside + j];
+ if (pOtherSide->bevel)
+ continue;
+ plane = &dplanes[pOtherSide->planenum^1];
+ ChopWindingInPlace (&w, plane->normal, plane->dist, 0);
+ }
+ if ( w )
+ {
+ for ( int j = 2; j < w->numpoints; j++ )
+ {
+ v0 = xform.VMul4x3(w->p[0]);
+ v1 = xform.VMul4x3(w->p[j-1]);
+ v2 = xform.VMul4x3(w->p[j]);
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+ g_RtEnv.AddTriangle(TRACE_ID_OPAQUE, v0, v1, v2, fullCoverage);
+ }
+ FreeWinding( w );
+ }
+ }
+}
+
+
+// recurse the bsp and build a list of brushes at the leaves under this node
+void GetBrushes_r( int node, CUtlVector<int> &list )
+{
+ if ( node < 0 )
+ {
+ int leafIndex = -1 - node;
+ // Add the solids in the leaf
+ for ( int i = 0; i < dleafs[leafIndex].numleafbrushes; i++ )
+ {
+ int brushIndex = dleafbrushes[dleafs[leafIndex].firstleafbrush + i];
+ if ( list.Find(brushIndex) < 0 )
+ {
+ list.AddToTail( brushIndex );
+ }
+ }
+ }
+ else
+ {
+ // recurse
+ dnode_t *pnode = dnodes + node;
+
+ GetBrushes_r( pnode->children[0], list );
+ GetBrushes_r( pnode->children[1], list );
+ }
+}
+
+
+void AddBrushes( dmodel_t *pModel, const VMatrix &xform )
+{
+ if ( pModel )
+ {
+ CUtlVector<int> brushList;
+ GetBrushes_r( pModel->headnode, brushList );
+ for ( int i = 0; i < brushList.Count(); i++ )
+ {
+ int ndxBrush = brushList[i];
+ AddBrushToRaytraceEnvironment( &dbrushes[ndxBrush], xform );
+ }
+ }
+}
+
+
+// Adds the brush entities that cast shadows to the raytrace environment
+void ExtractBrushEntityShadowCasters()
+{
+ for ( int i = 0; i < num_entities; i++ )
+ {
+ if ( IntForKey( &entities[i], "vrad_brush_cast_shadows" ) != 0 )
+ {
+ Vector origin;
+ QAngle angles;
+ GetVectorForKey( &entities[i], "origin", origin );
+ GetAnglesForKey( &entities[i], "angles", angles );
+ VMatrix xform;
+ xform.SetupMatrixOrgAngles( origin, angles );
+ AddBrushes( BrushmodelForEntity( &entities[i] ), xform );
+ }
+ }
+}
+
+void AddBrushesForRayTrace( void )
+{
+ if ( !nummodels )
+ return;
+
+ VMatrix identity;
+ identity.Identity();
+
+ CUtlVector<int> brushList;
+ GetBrushes_r ( dmodels[0].headnode, brushList );
+
+ for ( int i = 0; i < brushList.Size(); i++ )
+ {
+ dbrush_t *brush = &dbrushes[brushList[i]];
+ AddBrushToRaytraceEnvironment ( brush, identity );
+ }
+
+ for ( int i = 0; i < dmodels[0].numfaces; i++ )
+ {
+ int ndxFace = dmodels[0].firstface + i;
+ dface_t *face = &g_pFaces[ndxFace];
+
+ texinfo_t *tx = &texinfo[face->texinfo];
+ if ( !( tx->flags & SURF_SKY ) )
+ continue;
+
+ Vector points[MAX_POINTS_ON_WINDING];
+
+ for ( int j = 0; j < face->numedges; j++ )
+ {
+ int surfEdge = dsurfedges[face->firstedge + j];
+ short v;
+
+ if (surfEdge < 0)
+ v = dedges[-surfEdge].v[1];
+ else
+ v = dedges[surfEdge].v[0];
+
+ dvertex_t *dv = &dvertexes[v];
+ points[j] = dv->point;
+ }
+
+ for ( int j = 2; j < face->numedges; j++ )
+ {
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+ g_RtEnv.AddTriangle ( TRACE_ID_SKY, points[0], points[j - 1], points[j], fullCoverage );
+ }
+ }
+}
diff --git a/mp/src/utils/vrad/vismat.cpp b/mp/src/utils/vrad/vismat.cpp new file mode 100644 index 00000000..7491f69f --- /dev/null +++ b/mp/src/utils/vrad/vismat.cpp @@ -0,0 +1,483 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "vmpi.h"
+#ifdef MPI
+#include "messbuf.h"
+static MessageBuffer mb;
+#endif
+
+#define HALFBIT
+
+extern char source[MAX_PATH];
+extern char vismatfile[_MAX_PATH];
+extern char incrementfile[_MAX_PATH];
+extern qboolean incremental;
+
+/*
+===================================================================
+
+VISIBILITY MATRIX
+
+Determine which patches can see each other
+Use the PVS to accelerate if available
+===================================================================
+*/
+
+#define TEST_EPSILON 0.1
+#define PLANE_TEST_EPSILON 0.01 // patch must be this much in front of the plane to be considered "in front"
+#define PATCH_FACE_OFFSET 0.1 // push patch origins off from the face by this amount to avoid self collisions
+
+#define STREAM_SIZE 512
+
+class CTransferMaker
+{
+public:
+
+ CTransferMaker( transfer_t *all_transfers );
+ ~CTransferMaker();
+
+ FORCEINLINE void TestMakeTransfer( Vector start, Vector stop, int ndxShooter, int ndxReciever )
+ {
+ g_RtEnv.AddToRayStream( m_RayStream, start, stop, &m_pResults[m_nTests] );
+ m_pShooterPatches[m_nTests] = ndxShooter;
+ m_pRecieverPatches[m_nTests] = ndxReciever;
+ ++m_nTests;
+ }
+
+ void Finish();
+
+private:
+
+ int m_nTests;
+ RayTracingSingleResult *m_pResults;
+ int *m_pShooterPatches;
+ int *m_pRecieverPatches;
+ RayStream m_RayStream;
+ transfer_t *m_AllTransfers;
+};
+
+CTransferMaker::CTransferMaker( transfer_t *all_transfers ) :
+ m_AllTransfers( all_transfers ), m_nTests( 0 )
+{
+ m_pResults = (RayTracingSingleResult *)calloc( 1, MAX_PATCHES * sizeof ( RayTracingSingleResult ) );
+ m_pShooterPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
+ m_pRecieverPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
+}
+
+CTransferMaker::~CTransferMaker()
+{
+ free ( m_pResults );
+ free ( m_pShooterPatches );
+ free (m_pRecieverPatches );
+}
+
+void CTransferMaker::Finish()
+{
+ g_RtEnv.FinishRayStream( m_RayStream );
+ for ( int i = 0; i < m_nTests; ++i )
+ {
+ if ( m_pResults[i].HitID == -1 || m_pResults[i].HitDistance >= m_pResults[i].ray_length )
+ {
+ MakeTransfer( m_pShooterPatches[i], m_pRecieverPatches[i], m_AllTransfers );
+ }
+ }
+ m_nTests = 0;
+}
+
+
+dleaf_t* PointInLeaf (int iNode, Vector const& point)
+{
+ if ( iNode < 0 )
+ return &dleafs[ (-1-iNode) ];
+
+ dnode_t *node = &dnodes[iNode];
+ dplane_t *plane = &dplanes[ node->planenum ];
+
+ float dist = DotProduct (point, plane->normal) - plane->dist;
+ if ( dist > TEST_EPSILON )
+ {
+ return PointInLeaf( node->children[0], point );
+ }
+ else if ( dist < -TEST_EPSILON )
+ {
+ return PointInLeaf( node->children[1], point );
+ }
+ else
+ {
+ dleaf_t *pTest = PointInLeaf( node->children[0], point );
+ if ( pTest->cluster != -1 )
+ return pTest;
+
+ return PointInLeaf( node->children[1], point );
+ }
+}
+
+
+int ClusterFromPoint( Vector const& point )
+{
+ dleaf_t *leaf = PointInLeaf( 0, point );
+
+ return leaf->cluster;
+}
+
+void PvsForOrigin (Vector& org, byte *pvs)
+{
+ int visofs;
+ int cluster;
+
+ if (!visdatasize)
+ {
+ memset (pvs, 255, (dvis->numclusters+7)/8 );
+ return;
+ }
+
+ cluster = ClusterFromPoint( org );
+ if ( cluster < 0 )
+ {
+ visofs = -1;
+ }
+ else
+ {
+ visofs = dvis->bitofs[ cluster ][DVIS_PVS];
+ }
+
+ if (visofs == -1)
+ Error ("visofs == -1");
+
+ DecompressVis (&dvisdata[visofs], pvs);
+}
+
+
+void TestPatchToPatch( int ndxPatch1, int ndxPatch2, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
+{
+ Vector tmp;
+
+ //
+ // get patches
+ //
+ if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
+ return;
+
+ CPatch *patch = &g_Patches.Element( ndxPatch1 );
+ CPatch *patch2 = &g_Patches.Element( ndxPatch2 );
+
+ if (patch2->child1 != g_Patches.InvalidIndex() )
+ {
+ // check to see if we should use a child node instead
+
+ VectorSubtract( patch->origin, patch2->origin, tmp );
+ // SQRT( 1/4 )
+ // FIXME: should be based on form-factor (ie. include visible angle, etc)
+ if ( DotProduct(tmp, tmp) * 0.0625 < patch2->area )
+ {
+ TestPatchToPatch( ndxPatch1, patch2->child1, head, transfers, transferMaker, iThread );
+ TestPatchToPatch( ndxPatch1, patch2->child2, head, transfers, transferMaker, iThread );
+ return;
+ }
+ }
+
+ // check vis between patch and patch2
+ // if bit has not already been set
+ // && v2 is not behind light plane
+ // && v2 is visible from v1
+ if ( DotProduct( patch2->origin, patch->normal ) > patch->planeDist + PLANE_TEST_EPSILON )
+ {
+ // push out origins from face so that don't intersect their owners
+ Vector p1, p2;
+ VectorAdd( patch->origin, patch->normal, p1 );
+ VectorAdd( patch2->origin, patch2->normal, p2 );
+ transferMaker.TestMakeTransfer( p1, p2, ndxPatch1, ndxPatch2 );
+ }
+}
+
+
+/*
+==============
+TestPatchToFace
+
+Sets vis bits for all patches in the face
+==============
+*/
+void TestPatchToFace (unsigned patchnum, int facenum, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
+{
+ if( faceParents.Element( facenum ) == g_Patches.InvalidIndex() || patchnum == g_Patches.InvalidIndex() )
+ return;
+
+ CPatch *patch = &g_Patches.Element( patchnum );
+ CPatch *patch2 = &g_Patches.Element( faceParents.Element( facenum ) );
+
+ // if emitter is behind that face plane, skip all patches
+
+ CPatch *pNextPatch;
+
+ if ( patch2 && DotProduct(patch->origin, patch2->normal) > patch2->planeDist + PLANE_TEST_EPSILON )
+ {
+ // we need to do a real test
+ for( ; patch2; patch2 = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( patch2->ndxNextParent != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch2->ndxNextParent );
+ }
+
+ /*
+ // skip patches too far away
+ VectorSubtract( patch->origin, patch2->origin, tmp );
+ if (DotProduct( tmp, tmp ) > 512 * 512)
+ continue;
+ */
+
+ int ndxPatch2 = patch2 - g_Patches.Base();
+ TestPatchToPatch( patchnum, ndxPatch2, head, transfers, transferMaker, iThread );
+ }
+ }
+}
+
+
+struct ClusterDispList_t
+{
+ CUtlVector<int> dispFaces;
+};
+
+static CUtlVector<ClusterDispList_t> g_ClusterDispFaces;
+
+//-----------------------------------------------------------------------------
+// Helps us find all displacements associated with a particular cluster
+//-----------------------------------------------------------------------------
+void AddDispsToClusterTable( void )
+{
+ g_ClusterDispFaces.SetCount( g_ClusterLeaves.Count() );
+
+ //
+ // add displacement faces to the cluster table
+ //
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ // search for displacement faces
+ if( g_pFaces[ndxFace].dispinfo == -1 )
+ continue;
+
+ //
+ // get the clusters associated with the face
+ //
+ if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
+ {
+ CPatch *pNextPatch = NULL;
+ for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( pPatch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( pPatch->ndxNext );
+ }
+
+ if( pPatch->clusterNumber != g_Patches.InvalidIndex() )
+ {
+ int ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.Find( ndxFace );
+ if( ndxDisp == -1 )
+ {
+ ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.AddToTail();
+ g_ClusterDispFaces[pPatch->clusterNumber].dispFaces[ndxDisp] = ndxFace;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/*
+==============
+BuildVisRow
+
+Calc vis bits from a single patch
+==============
+*/
+void BuildVisRow (int patchnum, byte *pvs, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
+{
+ int j, k, l, leafIndex;
+ CPatch *patch;
+ dleaf_t *leaf;
+ byte face_tested[MAX_MAP_FACES];
+ byte disp_tested[MAX_MAP_FACES];
+
+ patch = &g_Patches.Element( patchnum );
+
+ memset( face_tested, 0, numfaces ) ;
+ memset( disp_tested, 0, numfaces );
+
+ for (j=0; j<dvis->numclusters; j++)
+ {
+ if ( ! ( pvs[(j)>>3] & (1<<((j)&7)) ) )
+ {
+ continue; // not in pvs
+ }
+
+ for ( leafIndex = 0; leafIndex < g_ClusterLeaves[j].leafCount; leafIndex++ )
+ {
+ leaf = dleafs + g_ClusterLeaves[j].leafs[leafIndex];
+
+ for (k=0 ; k<leaf->numleaffaces; k++)
+ {
+ l = dleaffaces[leaf->firstleafface + k];
+ // faces can be marksurfed by multiple leaves, but
+ // don't bother testing again
+ if (face_tested[l])
+ {
+ continue;
+ }
+ face_tested[l] = 1;
+
+ // don't check patches on the same face
+ if (patch->faceNumber == l)
+ continue;
+ TestPatchToFace (patchnum, l, head, transfers, transferMaker, iThread );
+ }
+ }
+
+ int dispCount = g_ClusterDispFaces[j].dispFaces.Size();
+ for( int ndxDisp = 0; ndxDisp < dispCount; ndxDisp++ )
+ {
+ int ndxFace = g_ClusterDispFaces[j].dispFaces[ndxDisp];
+ if( disp_tested[ndxFace] )
+ continue;
+
+ disp_tested[ndxFace] = 1;
+
+ // don't check patches on the same face
+ if( patch->faceNumber == ndxFace )
+ continue;
+
+ TestPatchToFace( patchnum, ndxFace, head, transfers, transferMaker, iThread );
+ }
+ }
+
+
+ // Msg("%d) Transfers: %5d\n", patchnum, patch->numtransfers);
+}
+
+
+
+/*
+===========
+BuildVisLeafs
+
+ This is run by multiple threads
+===========
+*/
+
+transfer_t* BuildVisLeafs_Start()
+{
+ return (transfer_t *)calloc( 1, MAX_PATCHES * sizeof( transfer_t ) );
+}
+
+
+// If PatchCB is non-null, it is called after each row is generated (used by MPI).
+void BuildVisLeafs_Cluster(
+ int threadnum,
+ transfer_t *transfers,
+ int iCluster,
+ void (*PatchCB)(int iThread, int patchnum, CPatch *patch)
+ )
+{
+ byte pvs[(MAX_MAP_CLUSTERS+7)/8];
+ CPatch *patch;
+ int head;
+ unsigned patchnum;
+
+ DecompressVis( &dvisdata[ dvis->bitofs[ iCluster ][DVIS_PVS] ], pvs);
+ head = 0;
+
+ CTransferMaker transferMaker( transfers );
+
+ // light every patch in the cluster
+ if( clusterChildren.Element( iCluster ) != clusterChildren.InvalidIndex() )
+ {
+ CPatch *pNextPatch;
+ for( patch = &g_Patches.Element( clusterChildren.Element( iCluster ) ); patch; patch = pNextPatch )
+ {
+ //
+ // next patch
+ //
+ pNextPatch = NULL;
+ if( patch->ndxNextClusterChild != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( patch->ndxNextClusterChild );
+ }
+
+ patchnum = patch - g_Patches.Base();
+
+ // build to all other world clusters
+ BuildVisRow (patchnum, pvs, head, transfers, transferMaker, threadnum );
+ transferMaker.Finish();
+
+ // do the transfers
+ MakeScales( patchnum, transfers );
+
+ // Let MPI aggregate the data if it's being used.
+ if ( PatchCB )
+ PatchCB( threadnum, patchnum, patch );
+ }
+ }
+}
+
+
+void BuildVisLeafs_End( transfer_t *transfers )
+{
+ free( transfers );
+}
+
+
+void BuildVisLeafs( int threadnum, void *pUserData )
+{
+ transfer_t *transfers = BuildVisLeafs_Start();
+
+ while ( 1 )
+ {
+ //
+ // build a minimal BSP tree that only
+ // covers areas relevent to the PVS
+ //
+ // JAY: Now this returns a cluster index
+ int iCluster = GetThreadWork();
+ if ( iCluster == -1 )
+ break;
+
+ BuildVisLeafs_Cluster( threadnum, transfers, iCluster, NULL );
+ }
+
+ BuildVisLeafs_End( transfers );
+}
+
+
+/*
+==============
+BuildVisMatrix
+==============
+*/
+void BuildVisMatrix (void)
+{
+ if ( g_bUseMPI )
+ {
+ RunMPIBuildVisLeafs();
+ }
+ else
+ {
+ RunThreadsOn (dvis->numclusters, true, BuildVisLeafs);
+ }
+}
+
+void FreeVisMatrix (void)
+{
+
+}
diff --git a/mp/src/utils/vrad/vismat.h b/mp/src/utils/vrad/vismat.h new file mode 100644 index 00000000..74e0ba4c --- /dev/null +++ b/mp/src/utils/vrad/vismat.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VISMAT_H
+#define VISMAT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+
+void BuildVisLeafs( int threadnum );
+
+
+// MPI uses these.
+struct transfer_t;
+transfer_t* BuildVisLeafs_Start();
+
+// If PatchCB is non-null, it is called after each row is generated (used by MPI).
+void BuildVisLeafs_Cluster(
+ int threadnum,
+ transfer_t *transfers,
+ int iCluster,
+ void (*PatchCB)(int iThread, int patchnum, CPatch *patch) );
+
+void BuildVisLeafs_End( transfer_t *transfers );
+
+
+
+#endif // VISMAT_H
diff --git a/mp/src/utils/vrad/vrad.cpp b/mp/src/utils/vrad/vrad.cpp new file mode 100644 index 00000000..854956ec --- /dev/null +++ b/mp/src/utils/vrad/vrad.cpp @@ -0,0 +1,2936 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+// vrad.c
+
+#include "vrad.h"
+#include "physdll.h"
+#include "lightmap.h"
+#include "tier1/strtools.h"
+#include "vmpi.h"
+#include "macro_texture.h"
+#include "vmpi_tools_shared.h"
+#include "leaf_ambient_lighting.h"
+#include "tools_minidump.h"
+#include "loadcmdline.h"
+#include "byteswap.h"
+
+#define ALLOWDEBUGOPTIONS (0 || _DEBUG)
+
+static FileHandle_t pFpTrans = NULL;
+
+/*
+
+NOTES
+-----
+
+every surface must be divided into at least two patches each axis
+
+*/
+
+CUtlVector<CPatch> g_Patches;
+CUtlVector<int> g_FacePatches; // constains all patches, children first
+CUtlVector<int> faceParents; // contains only root patches, use next parent to iterate
+CUtlVector<int> clusterChildren;
+CUtlVector<Vector> emitlight;
+CUtlVector<bumplights_t> addlight;
+
+int num_sky_cameras;
+sky_camera_t sky_cameras[MAX_MAP_AREAS];
+int area_sky_cameras[MAX_MAP_AREAS];
+
+entity_t *face_entity[MAX_MAP_FACES];
+Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels
+int fakeplanes;
+
+unsigned numbounce = 100; // 25; /* Originally this was 8 */
+
+float maxchop = 4; // coarsest allowed number of luxel widths for a patch
+float minchop = 4; // "-chop" tightest number of luxel widths for a patch, used on edges
+float dispchop = 8.0f; // number of luxel widths for a patch
+float g_MaxDispPatchRadius = 1500.0f; // Maximum radius allowed for displacement patches
+qboolean g_bDumpPatches;
+bool bDumpNormals = false;
+bool g_bDumpRtEnv = false;
+bool bRed2Black = true;
+bool g_bFastAmbient = false;
+bool g_bNoSkyRecurse = false;
+
+int junk;
+
+Vector ambient( 0, 0, 0 );
+
+float lightscale = 1.0;
+float dlight_threshold = 0.1; // was DIRECT_LIGHT constant
+
+char source[MAX_PATH] = "";
+char platformPath[MAX_PATH] = "";
+
+char level_name[MAX_PATH] = ""; // map filename, without extension or path info
+
+char global_lights[MAX_PATH] = "";
+char designer_lights[MAX_PATH] = "";
+char level_lights[MAX_PATH] = "";
+
+char vismatfile[_MAX_PATH] = "";
+char incrementfile[_MAX_PATH] = "";
+
+IIncremental *g_pIncremental = 0;
+bool g_bInterrupt = false; // Wsed with background lighting in WC. Tells VRAD
+ // to stop lighting.
+float g_SunAngularExtent=0.0;
+
+float g_flSkySampleScale = 1.0;
+
+bool g_bLargeDispSampleRadius = false;
+
+bool g_bOnlyStaticProps = false;
+bool g_bShowStaticPropNormals = false;
+
+
+float gamma = 0.5;
+float indirect_sun = 1.0;
+float reflectivityScale = 1.0;
+qboolean do_extra = true;
+bool debug_extra = false;
+qboolean do_fast = false;
+qboolean do_centersamples = false;
+int extrapasses = 4;
+float smoothing_threshold = 0.7071067; // cos(45.0*(M_PI/180))
+// Cosine of smoothing angle(in radians)
+float coring = 1.0; // Light threshold to force to blackness(minimizes lightmaps)
+qboolean texscale = true;
+int dlight_map = 0; // Setting to 1 forces direct lighting into different lightmap than radiosity
+
+float luxeldensity = 1.0;
+unsigned num_degenerate_faces;
+
+qboolean g_bLowPriority = false;
+qboolean g_bLogHashData = false;
+bool g_bNoDetailLighting = false;
+double g_flStartTime;
+bool g_bStaticPropLighting = false;
+bool g_bStaticPropPolys = false;
+bool g_bTextureShadows = false;
+bool g_bDisablePropSelfShadowing = false;
+
+
+CUtlVector<byte> g_FacesVisibleToLights;
+
+RayTracingEnvironment g_RtEnv;
+
+dface_t *g_pFaces=0;
+
+// this is a list of material names used on static props which shouldn't cast shadows. a
+// sequential search is used since we allow substring matches. its not time critical, and this
+// functionality is a stopgap until vrad starts reading .vmt files.
+CUtlVector<char const *> g_NonShadowCastingMaterialStrings;
+/*
+===================================================================
+
+MISC
+
+===================================================================
+*/
+
+
+int leafparents[MAX_MAP_LEAFS];
+int nodeparents[MAX_MAP_NODES];
+
+void MakeParents (int nodenum, int parent)
+{
+ int i, j;
+ dnode_t *node;
+
+ nodeparents[nodenum] = parent;
+ node = &dnodes[nodenum];
+
+ for (i=0 ; i<2 ; i++)
+ {
+ j = node->children[i];
+ if (j < 0)
+ leafparents[-j - 1] = nodenum;
+ else
+ MakeParents (j, nodenum);
+ }
+}
+
+
+/*
+===================================================================
+
+ TEXTURE LIGHT VALUES
+
+===================================================================
+*/
+
+typedef struct
+{
+ char name[256];
+ Vector value;
+ char *filename;
+} texlight_t;
+
+#define MAX_TEXLIGHTS 128
+
+texlight_t texlights[MAX_TEXLIGHTS];
+int num_texlights;
+
+/*
+============
+ReadLightFile
+============
+*/
+void ReadLightFile (char *filename)
+{
+ char buf[1024];
+ int file_texlights = 0;
+
+ FileHandle_t f = g_pFileSystem->Open( filename, "r" );
+ if (!f)
+ {
+ Warning("Warning: Couldn't open texlight file %s.\n", filename);
+ return;
+ }
+
+ Msg("[Reading texlights from '%s']\n", filename);
+ while ( CmdLib_FGets( buf, sizeof( buf ), f ) )
+ {
+ // check ldr/hdr
+ char * scan = buf;
+ if ( !strnicmp( "hdr:", scan, 4) )
+ {
+ scan += 4;
+ if ( ! g_bHDR )
+ {
+ continue;
+ }
+ }
+ if ( !strnicmp( "ldr:", scan, 4) )
+ {
+ scan += 4;
+ if ( g_bHDR )
+ {
+ continue;
+ }
+ }
+
+ scan += strspn( scan, " \t" );
+ char NoShadName[1024];
+ if ( sscanf(scan,"noshadow %s",NoShadName)==1)
+ {
+ char * dot = strchr( NoShadName, '.' );
+ if ( dot ) // if they specify .vmt, kill it
+ * dot = 0;
+ //printf("add %s as a non shadow casting material\n",NoShadName);
+ g_NonShadowCastingMaterialStrings.AddToTail( strdup( NoShadName ));
+ }
+ else if ( sscanf( scan, "forcetextureshadow %s", NoShadName ) == 1 )
+ {
+ //printf("add %s as a non shadow casting material\n",NoShadName);
+ ForceTextureShadowsOnModel( NoShadName );
+ }
+ else
+ {
+ char szTexlight[256];
+ Vector value;
+ if ( num_texlights == MAX_TEXLIGHTS )
+ Error ("Too many texlights, max = %d", MAX_TEXLIGHTS);
+
+ int argCnt = sscanf (scan, "%s ",szTexlight );
+
+ if( argCnt != 1 )
+ {
+ if ( strlen( scan ) > 4 )
+ Msg( "ignoring bad texlight '%s' in %s", scan, filename );
+ continue;
+ }
+
+ LightForString( scan + strlen( szTexlight ) + 1, value );
+
+ int j = 0;
+ for( j; j < num_texlights; j ++ )
+ {
+ if ( strcmp( texlights[j].name, szTexlight ) == 0 )
+ {
+ if ( strcmp( texlights[j].filename, filename ) == 0 )
+ {
+ Msg( "ERROR\a: Duplication of '%s' in file '%s'!\n",
+ texlights[j].name, texlights[j].filename );
+ }
+ else if ( texlights[j].value[0] != value[0]
+ || texlights[j].value[1] != value[1]
+ || texlights[j].value[2] != value[2] )
+ {
+ Warning( "Warning: Overriding '%s' from '%s' with '%s'!\n",
+ texlights[j].name, texlights[j].filename, filename );
+ }
+ else
+ {
+ Warning( "Warning: Redundant '%s' def in '%s' AND '%s'!\n",
+ texlights[j].name, texlights[j].filename, filename );
+ }
+ break;
+ }
+ }
+ strcpy( texlights[j].name, szTexlight );
+ VectorCopy( value, texlights[j].value );
+ texlights[j].filename = filename;
+ file_texlights ++;
+
+ num_texlights = max( num_texlights, j + 1 );
+ }
+ }
+ qprintf ( "[%i texlights parsed from '%s']\n\n", file_texlights, filename);
+ g_pFileSystem->Close( f );
+}
+
+
+/*
+============
+LightForTexture
+============
+*/
+void LightForTexture( const char *name, Vector& result )
+{
+ int i;
+
+ result[ 0 ] = result[ 1 ] = result[ 2 ] = 0;
+
+ char baseFilename[ MAX_PATH ];
+
+ if ( Q_strncmp( "maps/", name, 5 ) == 0 )
+ {
+ // this might be a patch texture for cubemaps. try to parse out the original filename.
+ if ( Q_strncmp( level_name, name + 5, Q_strlen( level_name ) ) == 0 )
+ {
+ const char *base = name + 5 + Q_strlen( level_name );
+ if ( *base == '/' )
+ {
+ ++base; // step past the path separator
+
+ // now we've gotten rid of the 'maps/level_name/' part, so we're left with
+ // 'originalName_%d_%d_%d'.
+ strcpy( baseFilename, base );
+ bool foundSeparators = true;
+ for ( int i=0; i<3; ++i )
+ {
+ char *underscore = Q_strrchr( baseFilename, '_' );
+ if ( underscore && *underscore )
+ {
+ *underscore = '\0';
+ }
+ else
+ {
+ foundSeparators = false;
+ }
+ }
+
+ if ( foundSeparators )
+ {
+ name = baseFilename;
+ }
+ }
+ }
+ }
+
+ for (i=0 ; i<num_texlights ; i++)
+ {
+ if (!Q_strcasecmp (name, texlights[i].name))
+ {
+ VectorCopy( texlights[i].value, result );
+ return;
+ }
+ }
+}
+
+/*
+=======================================================================
+
+MAKE FACES
+
+=======================================================================
+*/
+
+/*
+=============
+WindingFromFace
+=============
+*/
+winding_t *WindingFromFace (dface_t *f, Vector& origin )
+{
+ int i;
+ int se;
+ dvertex_t *dv;
+ int v;
+ winding_t *w;
+
+ w = AllocWinding (f->numedges);
+ w->numpoints = f->numedges;
+
+ for (i=0 ; i<f->numedges ; i++)
+ {
+ se = dsurfedges[f->firstedge + i];
+ if (se < 0)
+ v = dedges[-se].v[1];
+ else
+ v = dedges[se].v[0];
+
+ dv = &dvertexes[v];
+ VectorAdd (dv->point, origin, w->p[i]);
+ }
+
+ RemoveColinearPoints (w);
+
+ return w;
+}
+
+/*
+=============
+BaseLightForFace
+=============
+*/
+void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity )
+{
+ texinfo_t *tx;
+ dtexdata_t *texdata;
+
+ //
+ // check for light emited by texture
+ //
+ tx = &texinfo[f->texinfo];
+ texdata = &dtexdata[tx->texdata];
+
+ LightForTexture (TexDataStringTable_GetString( texdata->nameStringTableID ), light);
+
+
+ *parea = texdata->height * texdata->width;
+
+ VectorScale( texdata->reflectivity, reflectivityScale, reflectivity );
+
+ // always keep this less than 1 or the solution will not converge
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( reflectivity[i] > 0.99 )
+ reflectivity[i] = 0.99;
+ }
+}
+
+qboolean IsSky (dface_t *f)
+{
+ texinfo_t *tx;
+
+ tx = &texinfo[f->texinfo];
+ if (tx->flags & SURF_SKY)
+ return true;
+ return false;
+}
+
+#ifdef STATIC_FOG
+/*=============
+IsFog
+=============*/
+qboolean IsFog( dface_t *f )
+{
+ texinfo_t *tx;
+
+ tx = &texinfo[f->texinfo];
+
+ // % denotes a fog texture
+ if( tx->texture[0] == '%' )
+ return true;
+
+ return false;
+}
+#endif
+
+
+void ProcessSkyCameras()
+{
+ int i;
+ num_sky_cameras = 0;
+ for (i = 0; i < numareas; ++i)
+ {
+ area_sky_cameras[i] = -1;
+ }
+
+ for (i = 0; i < num_entities; ++i)
+ {
+ entity_t *e = &entities[i];
+ const char *name = ValueForKey (e, "classname");
+ if (stricmp (name, "sky_camera"))
+ continue;
+
+ Vector origin;
+ GetVectorForKey( e, "origin", origin );
+ int node = PointLeafnum( origin );
+ int area = -1;
+ if (node >= 0 && node < numleafs) area = dleafs[node].area;
+ float scale = FloatForKey( e, "scale" );
+
+ if (scale > 0.0f)
+ {
+ sky_cameras[num_sky_cameras].origin = origin;
+ sky_cameras[num_sky_cameras].sky_to_world = scale;
+ sky_cameras[num_sky_cameras].world_to_sky = 1.0f / scale;
+ sky_cameras[num_sky_cameras].area = area;
+
+ if (area >= 0 && area < numareas)
+ {
+ area_sky_cameras[area] = num_sky_cameras;
+ }
+
+ ++num_sky_cameras;
+ }
+ }
+
+}
+
+
+/*
+=============
+MakePatchForFace
+=============
+*/
+float totalarea;
+void MakePatchForFace (int fn, winding_t *w)
+{
+ dface_t *f = g_pFaces + fn;
+ float area;
+ CPatch *patch;
+ Vector centroid(0,0,0);
+ int i, j;
+ texinfo_t *tx;
+
+ // get texture info
+ tx = &texinfo[f->texinfo];
+
+ // No patches at all for fog!
+#ifdef STATIC_FOG
+ if ( IsFog( f ) )
+ return;
+#endif
+
+ // the sky needs patches or the form factors don't work out correctly
+ // if (IsSky( f ) )
+ // return;
+
+ area = WindingArea (w);
+ if (area <= 0)
+ {
+ num_degenerate_faces++;
+ // Msg("degenerate face\n");
+ return;
+ }
+
+ totalarea += area;
+
+ // get a patch
+ int ndxPatch = g_Patches.AddToTail();
+ patch = &g_Patches[ndxPatch];
+ memset( patch, 0, sizeof( CPatch ) );
+ patch->ndxNext = g_Patches.InvalidIndex();
+ patch->ndxNextParent = g_Patches.InvalidIndex();
+ patch->ndxNextClusterChild = g_Patches.InvalidIndex();
+ patch->child1 = g_Patches.InvalidIndex();
+ patch->child2 = g_Patches.InvalidIndex();
+ patch->parent = g_Patches.InvalidIndex();
+ patch->needsBumpmap = tx->flags & SURF_BUMPLIGHT ? true : false;
+
+ // link and save patch data
+ patch->ndxNext = g_FacePatches.Element( fn );
+ g_FacePatches[fn] = ndxPatch;
+// patch->next = face_g_Patches[fn];
+// face_g_Patches[fn] = patch;
+
+ // compute a separate scale for chop - since the patch "scale" is the texture scale
+ // we want textures with higher resolution lighting to be chopped up more
+ float chopscale[2];
+ chopscale[0] = chopscale[1] = 16.0f;
+ if ( texscale )
+ {
+ // Compute the texture "scale" in s,t
+ for( i=0; i<2; i++ )
+ {
+ patch->scale[i] = 0.0f;
+ chopscale[i] = 0.0f;
+ for( j=0; j<3; j++ )
+ {
+ patch->scale[i] +=
+ tx->textureVecsTexelsPerWorldUnits[i][j] *
+ tx->textureVecsTexelsPerWorldUnits[i][j];
+ chopscale[i] +=
+ tx->lightmapVecsLuxelsPerWorldUnits[i][j] *
+ tx->lightmapVecsLuxelsPerWorldUnits[i][j];
+ }
+ patch->scale[i] = sqrt( patch->scale[i] );
+ chopscale[i] = sqrt( chopscale[i] );
+ }
+ }
+ else
+ {
+ patch->scale[0] = patch->scale[1] = 1.0f;
+ }
+
+ patch->area = area;
+
+ patch->sky = IsSky( f );
+
+ // chop scaled up lightmaps coarser
+ patch->luxscale = ((chopscale[0]+chopscale[1])/2);
+ patch->chop = maxchop;
+
+
+#ifdef STATIC_FOG
+ patch->fog = FALSE;
+#endif
+
+ patch->winding = w;
+
+ patch->plane = &dplanes[f->planenum];
+
+ // make a new plane to adjust for origined bmodels
+ if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] )
+ {
+ dplane_t *pl;
+
+ // origin offset faces must create new planes
+ if (numplanes + fakeplanes >= MAX_MAP_PLANES)
+ {
+ Error ("numplanes + fakeplanes >= MAX_MAP_PLANES");
+ }
+ pl = &dplanes[numplanes + fakeplanes];
+ fakeplanes++;
+
+ *pl = *(patch->plane);
+ pl->dist += DotProduct (face_offset[fn], pl->normal);
+ patch->plane = pl;
+ }
+
+ patch->faceNumber = fn;
+ WindingCenter (w, patch->origin);
+
+ // Save "center" for generating the face normals later.
+ VectorSubtract( patch->origin, face_offset[fn], face_centroids[fn] );
+
+ VectorCopy( patch->plane->normal, patch->normal );
+
+ WindingBounds (w, patch->face_mins, patch->face_maxs);
+ VectorCopy( patch->face_mins, patch->mins );
+ VectorCopy( patch->face_maxs, patch->maxs );
+
+ BaseLightForFace( f, patch->baselight, &patch->basearea, patch->reflectivity );
+
+ // Chop all texlights very fine.
+ if ( !VectorCompare( patch->baselight, vec3_origin ) )
+ {
+ // patch->chop = do_extra ? maxchop / 2 : maxchop;
+ tx->flags |= SURF_LIGHT;
+ }
+
+ // get rid of do extra functionality on displacement surfaces
+ if( ValidDispFace( f ) )
+ {
+ patch->chop = maxchop;
+ }
+
+ // FIXME: If we wanted to add a dependency from vrad to the material system,
+ // we could do this. It would add a bunch of file accesses, though:
+
+ /*
+ // Check for a material var which would override the patch chop
+ bool bFound;
+ const char *pMaterialName = TexDataStringTable_GetString( dtexdata[ tx->texdata ].nameStringTableID );
+ MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, &bFound, false );
+ if ( bFound )
+ {
+ const char *pChopValue = GetMaterialVar( hMaterial, "%chop" );
+ if ( pChopValue )
+ {
+ float flChopValue;
+ if ( sscanf( pChopValue, "%f", &flChopValue ) > 0 )
+ {
+ patch->chop = flChopValue;
+ }
+ }
+ }
+ */
+}
+
+
+entity_t *EntityForModel (int modnum)
+{
+ int i;
+ char *s;
+ char name[16];
+
+ sprintf (name, "*%i", modnum);
+ // search the entities for one using modnum
+ for (i=0 ; i<num_entities ; i++)
+ {
+ s = ValueForKey (&entities[i], "model");
+ if (!strcmp (s, name))
+ return &entities[i];
+ }
+
+ return &entities[0];
+}
+
+/*
+=============
+MakePatches
+=============
+*/
+void MakePatches (void)
+{
+ int i, j;
+ dface_t *f;
+ int fn;
+ winding_t *w;
+ dmodel_t *mod;
+ Vector origin;
+ entity_t *ent;
+
+ ParseEntities ();
+ qprintf ("%i faces\n", numfaces);
+
+ for (i=0 ; i<nummodels ; i++)
+ {
+ mod = dmodels+i;
+ ent = EntityForModel (i);
+ VectorCopy (vec3_origin, origin);
+
+ // bmodels with origin brushes need to be offset into their
+ // in-use position
+ GetVectorForKey (ent, "origin", origin);
+
+ for (j=0 ; j<mod->numfaces ; j++)
+ {
+ fn = mod->firstface + j;
+ face_entity[fn] = ent;
+ VectorCopy (origin, face_offset[fn]);
+ f = &g_pFaces[fn];
+ if( f->dispinfo == -1 )
+ {
+ w = WindingFromFace (f, origin );
+ MakePatchForFace( fn, w );
+ }
+ }
+ }
+
+ if (num_degenerate_faces > 0)
+ {
+ qprintf("%d degenerate faces\n", num_degenerate_faces );
+ }
+
+ qprintf ("%i square feet [%.2f square inches]\n", (int)(totalarea/144), totalarea );
+
+ // make the displacement surface patches
+ StaticDispMgr()->MakePatches();
+}
+
+/*
+=======================================================================
+
+SUBDIVIDE
+
+=======================================================================
+*/
+
+
+//-----------------------------------------------------------------------------
+// Purpose: does this surface take/emit light
+//-----------------------------------------------------------------------------
+bool PreventSubdivision( CPatch *patch )
+{
+ dface_t *f = g_pFaces + patch->faceNumber;
+ texinfo_t *tx = &texinfo[f->texinfo];
+
+ if (tx->flags & SURF_NOCHOP)
+ return true;
+
+ if (tx->flags & SURF_NOLIGHT && !(tx->flags & SURF_LIGHT))
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: subdivide the "parent" patch
+//-----------------------------------------------------------------------------
+int CreateChildPatch( int nParentIndex, winding_t *pWinding, float flArea, const Vector &vecCenter )
+{
+ int nChildIndex = g_Patches.AddToTail();
+
+ CPatch *child = &g_Patches[nChildIndex];
+ CPatch *parent = &g_Patches[nParentIndex];
+
+ // copy all elements of parent patch to children
+ *child = *parent;
+
+ // Set up links
+ child->ndxNext = g_Patches.InvalidIndex();
+ child->ndxNextParent = g_Patches.InvalidIndex();
+ child->ndxNextClusterChild = g_Patches.InvalidIndex();
+ child->child1 = g_Patches.InvalidIndex();
+ child->child2 = g_Patches.InvalidIndex();
+ child->parent = nParentIndex;
+ child->m_IterationKey = 0;
+
+ child->winding = pWinding;
+ child->area = flArea;
+
+ VectorCopy( vecCenter, child->origin );
+ if ( ValidDispFace( g_pFaces + child->faceNumber ) )
+ {
+ // shouldn't get here anymore!!
+ Msg( "SubdividePatch: Error - Should not be here!\n" );
+ StaticDispMgr()->GetDispSurfNormal( child->faceNumber, child->origin, child->normal, true );
+ }
+ else
+ {
+ GetPhongNormal( child->faceNumber, child->origin, child->normal );
+ }
+
+ child->planeDist = child->plane->dist;
+ WindingBounds(child->winding, child->mins, child->maxs);
+
+ if ( !VectorCompare( child->baselight, vec3_origin ) )
+ {
+ // don't check edges on surf lights
+ return nChildIndex;
+ }
+
+ // Subdivide patch towards minchop if on the edge of the face
+ Vector total;
+ VectorSubtract( child->maxs, child->mins, total );
+ VectorScale( total, child->luxscale, total );
+ if ( child->chop > minchop && (total[0] < child->chop) && (total[1] < child->chop) && (total[2] < child->chop) )
+ {
+ for ( int i=0; i<3; ++i )
+ {
+ if ( (child->face_maxs[i] == child->maxs[i] || child->face_mins[i] == child->mins[i] )
+ && total[i] > minchop )
+ {
+ child->chop = max( minchop, child->chop / 2 );
+ break;
+ }
+ }
+ }
+
+ return nChildIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: subdivide the "parent" patch
+//-----------------------------------------------------------------------------
+void SubdividePatch( int ndxPatch )
+{
+ winding_t *w, *o1, *o2;
+ Vector total;
+ Vector split;
+ vec_t dist;
+ vec_t widest = -1;
+ int i, widest_axis = -1;
+ bool bSubdivide = false;
+
+ // get the current patch
+ CPatch *patch = &g_Patches.Element( ndxPatch );
+ if ( !patch )
+ return;
+
+ // never subdivide sky patches
+ if ( patch->sky )
+ return;
+
+ // get the patch winding
+ w = patch->winding;
+
+ // subdivide along the widest axis
+ VectorSubtract (patch->maxs, patch->mins, total);
+ VectorScale( total, patch->luxscale, total );
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( total[i] > widest )
+ {
+ widest_axis = i;
+ widest = total[i];
+ }
+
+ if ( (total[i] >= patch->chop) && (total[i] >= minchop) )
+ {
+ bSubdivide = true;
+ }
+ }
+
+ if ((!bSubdivide) && widest_axis != -1)
+ {
+ // make more square
+ if (total[widest_axis] > total[(widest_axis + 1) % 3] * 2 && total[widest_axis] > total[(widest_axis + 2) % 3] * 2)
+ {
+ if (patch->chop > minchop)
+ {
+ bSubdivide = true;
+ patch->chop = max( minchop, patch->chop / 2 );
+ }
+ }
+ }
+
+ if ( !bSubdivide )
+ return;
+
+ // split the winding
+ VectorCopy (vec3_origin, split);
+ split[widest_axis] = 1;
+ dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f;
+ ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2);
+
+ // calculate the area of the patches to see if they are "significant"
+ Vector center1, center2;
+ float area1 = WindingAreaAndBalancePoint( o1, center1 );
+ float area2 = WindingAreaAndBalancePoint( o2, center2 );
+
+ if( area1 == 0 || area2 == 0 )
+ {
+ Msg( "zero area child patch\n" );
+ return;
+ }
+
+ // create new child patches
+ int ndxChild1Patch = CreateChildPatch( ndxPatch, o1, area1, center1 );
+ int ndxChild2Patch = CreateChildPatch( ndxPatch, o2, area2, center2 );
+
+ // FIXME: This could go into CreateChildPatch if child1, child2 were stored in the patch as child[0], child[1]
+ patch = &g_Patches.Element( ndxPatch );
+ patch->child1 = ndxChild1Patch;
+ patch->child2 = ndxChild2Patch;
+
+ SubdividePatch( ndxChild1Patch );
+ SubdividePatch( ndxChild2Patch );
+}
+
+
+/*
+=============
+SubdividePatches
+=============
+*/
+void SubdividePatches (void)
+{
+ unsigned i, num;
+
+ if (numbounce == 0)
+ return;
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ qprintf ("%i patches before subdivision\n", uiPatchCount);
+
+ for (i = 0; i < uiPatchCount; i++)
+ {
+ CPatch *pCur = &g_Patches.Element( i );
+ pCur->planeDist = pCur->plane->dist;
+
+ pCur->ndxNextParent = faceParents.Element( pCur->faceNumber );
+ faceParents[pCur->faceNumber] = pCur - g_Patches.Base();
+ }
+
+ for (i=0 ; i< uiPatchCount; i++)
+ {
+ CPatch *patch = &g_Patches.Element( i );
+ patch->parent = -1;
+ if ( PreventSubdivision(patch) )
+ continue;
+
+ if (!do_fast)
+ {
+ if( g_pFaces[patch->faceNumber].dispinfo == -1 )
+ {
+ SubdividePatch( i );
+ }
+ else
+ {
+ StaticDispMgr()->SubdividePatch( i );
+ }
+ }
+ }
+
+ // fixup next pointers
+ for (i = 0; i < (unsigned)numfaces; i++)
+ {
+ g_FacePatches[i] = g_FacePatches.InvalidIndex();
+ }
+
+ uiPatchCount = g_Patches.Size();
+ for (i = 0; i < uiPatchCount; i++)
+ {
+ CPatch *pCur = &g_Patches.Element( i );
+ pCur->ndxNext = g_FacePatches.Element( pCur->faceNumber );
+ g_FacePatches[pCur->faceNumber] = pCur - g_Patches.Base();
+
+#if 0
+ CPatch *prev;
+ prev = face_g_Patches[g_Patches[i].faceNumber];
+ g_Patches[i].next = prev;
+ face_g_Patches[g_Patches[i].faceNumber] = &g_Patches[i];
+#endif
+ }
+
+ // Cache off the leaf number:
+ // We have to do this after subdivision because some patches span leaves.
+ // (only the faces for model #0 are split by it's BSP which is what governs the PVS, and the leaves we're interested in)
+ // Sub models (1-255) are only split for the BSP that their model forms.
+ // When those patches are subdivided their origins can end up in a different leaf.
+ // The engine will split (clip) those faces at run time to the world BSP because the models
+ // are dynamic and can be moved. In the software renderer, they must be split exactly in order
+ // to sort per polygon.
+ for ( i = 0; i < uiPatchCount; i++ )
+ {
+ g_Patches[i].clusterNumber = ClusterFromPoint( g_Patches[i].origin );
+
+ //
+ // test for point in solid space (can happen with detail and displacement surfaces)
+ //
+ if( g_Patches[i].clusterNumber == -1 )
+ {
+ for( int j = 0; j < g_Patches[i].winding->numpoints; j++ )
+ {
+ int clusterNumber = ClusterFromPoint( g_Patches[i].winding->p[j] );
+ if( clusterNumber != -1 )
+ {
+ g_Patches[i].clusterNumber = clusterNumber;
+ break;
+ }
+ }
+ }
+ }
+
+ // build the list of patches that need to be lit
+ for ( num = 0; num < uiPatchCount; num++ )
+ {
+ // do them in reverse order
+ i = uiPatchCount - num - 1;
+
+ // skip patches with children
+ CPatch *pCur = &g_Patches.Element( i );
+ if( pCur->child1 == g_Patches.InvalidIndex() )
+ {
+ if( pCur->clusterNumber != - 1 )
+ {
+ pCur->ndxNextClusterChild = clusterChildren.Element( pCur->clusterNumber );
+ clusterChildren[pCur->clusterNumber] = pCur - g_Patches.Base();
+ }
+ }
+
+#if 0
+ if (g_Patches[i].child1 == g_Patches.InvalidIndex() )
+ {
+ if( g_Patches[i].clusterNumber != -1 )
+ {
+ g_Patches[i].nextclusterchild = cluster_children[g_Patches[i].clusterNumber];
+ cluster_children[g_Patches[i].clusterNumber] = &g_Patches[i];
+ }
+ }
+#endif
+ }
+
+ qprintf ("%i patches after subdivision\n", uiPatchCount);
+}
+
+
+//=====================================================================
+
+/*
+=============
+MakeScales
+
+ This is the primary time sink.
+ It can be run multi threaded.
+=============
+*/
+int total_transfer;
+int max_transfer;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Computes the form factor from a polygon patch to a differential patch
+// using formula 81 of Philip Dutre's Global Illumination Compendium,
+// [email protected], http://www.graphics.cornell.edu/~phil/GI/
+//-----------------------------------------------------------------------------
+float FormFactorPolyToDiff ( CPatch *pPolygon, CPatch* pDifferential )
+{
+ winding_t *pWinding = pPolygon->winding;
+
+ float flFormFactor = 0.0f;
+
+ for ( int iPoint = 0; iPoint < pWinding->numpoints; iPoint++ )
+ {
+ int iNextPoint = ( iPoint < pWinding->numpoints - 1 ) ? iPoint + 1 : 0;
+
+ Vector vGammaVector, vVector1, vVector2;
+ VectorSubtract( pWinding->p[ iPoint ], pDifferential->origin, vVector1 );
+ VectorSubtract( pWinding->p[ iNextPoint ], pDifferential->origin, vVector2 );
+ VectorNormalize( vVector1 );
+ VectorNormalize( vVector2 );
+ CrossProduct( vVector1, vVector2, vGammaVector );
+ float flSinAlpha = VectorNormalize( vGammaVector );
+ if (flSinAlpha < -1.0f || flSinAlpha > 1.0f)
+ return 0.0f;
+ vGammaVector *= asin( flSinAlpha );
+
+ flFormFactor += DotProduct( vGammaVector, pDifferential->normal );
+ }
+
+ flFormFactor *= ( 0.5f / pPolygon->area ); // divide by pi later, multiply by area later
+
+ return flFormFactor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Computes the form factor from a differential element to a differential
+// element. This is okay when the distance between patches is 5 times
+// greater than patch size. Lecture slides by Pat Hanrahan,
+// http://graphics.stanford.edu/courses/cs348b-00/lectures/lecture17/radiosity.2.pdf
+//-----------------------------------------------------------------------------
+float FormFactorDiffToDiff ( CPatch *pDiff1, CPatch* pDiff2 )
+{
+ Vector vDelta;
+ VectorSubtract( pDiff1->origin, pDiff2->origin, vDelta );
+ float flLength = VectorNormalize( vDelta );
+
+ return -DotProduct( vDelta, pDiff1->normal ) * DotProduct( vDelta, pDiff2->normal ) / ( flLength * flLength );
+}
+
+
+
+void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers )
+//void MakeTransfer (CPatch *patch, CPatch *patch2, transfer_t *all_transfers )
+{
+ Vector delta;
+ vec_t scale;
+ float trans;
+ transfer_t *transfer;
+
+ //
+ // get patches
+ //
+ if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
+ return;
+
+ CPatch *pPatch1 = &g_Patches.Element( ndxPatch1 );
+ CPatch *pPatch2 = &g_Patches.Element( ndxPatch2 );
+
+ if (IsSky( &g_pFaces[ pPatch2->faceNumber ] ) )
+ return;
+
+ // overflow check!
+ if ( pPatch1->numtransfers >= MAX_PATCHES)
+ {
+ return;
+ }
+
+ // hack for patch areas that area <= 0 (degenerate)
+ if ( pPatch2->area <= 0)
+ {
+ return;
+ }
+
+ transfer = &all_transfers[pPatch1->numtransfers];
+
+ scale = FormFactorDiffToDiff( pPatch2, pPatch1 );
+
+ // patch normals may be > 90 due to smoothing groups
+ if (scale <= 0)
+ {
+ //Msg("scale <= 0\n");
+ return;
+ }
+
+ // Test 5 times rule
+ Vector vDelta;
+ VectorSubtract( pPatch1->origin, pPatch2->origin, vDelta );
+ float flThreshold = ( M_PI * 0.04 ) * DotProduct( vDelta, vDelta );
+
+ if (flThreshold < pPatch2->area)
+ {
+ scale = FormFactorPolyToDiff( pPatch2, pPatch1 );
+ if (scale <= 0.0)
+ return;
+ }
+
+ trans = (pPatch2->area*scale);
+
+ if (trans <= TRANSFER_EPSILON)
+ {
+ return;
+ }
+
+ transfer->patch = pPatch2 - g_Patches.Base();
+
+ // FIXME: why is this not trans?
+ transfer->transfer = trans;
+
+#if 0
+ // DEBUG! Dump patches and transfer connection for displacements. This creates a lot of data, so only
+ // use it when you really want it - that is why it is #if-ed out.
+ if ( g_bDumpPatches )
+ {
+ if ( !pFpTrans )
+ {
+ pFpTrans = g_pFileSystem->Open( "trans.txt", "w" );
+ }
+ Vector light = pPatch1->totallight.light[0] + pPatch1->directlight;
+ WriteWinding( pFpTrans, pPatch1->winding, light );
+ light = pPatch2->totallight.light[0] + pPatch2->directlight;
+ WriteWinding( pFpTrans, pPatch2->winding, light );
+ WriteLine( pFpTrans, pPatch1->origin, pPatch2->origin, Vector( 255, 0, 255 ) );
+ }
+#endif
+
+ pPatch1->numtransfers++;
+}
+
+
+void MakeScales ( int ndxPatch, transfer_t *all_transfers )
+{
+ int j;
+ float total;
+ transfer_t *t, *t2;
+ total = 0;
+
+ if( ndxPatch == g_Patches.InvalidIndex() )
+ return;
+ CPatch *patch = &g_Patches.Element( ndxPatch );
+
+ // copy the transfers out
+ if (patch->numtransfers)
+ {
+ if (patch->numtransfers > max_transfer)
+ {
+ max_transfer = patch->numtransfers;
+ }
+
+
+ patch->transfers = ( transfer_t* )calloc (1, patch->numtransfers * sizeof(transfer_t));
+ if (!patch->transfers)
+ Error ("Memory allocation failure");
+
+ // get total transfer energy
+ t2 = all_transfers;
+
+ // overflow check!
+ for (j=0 ; j<patch->numtransfers ; j++, t2++)
+ {
+ total += t2->transfer;
+ }
+
+ // the total transfer should be PI, but we need to correct errors due to overlaping surfaces
+ if (total > M_PI)
+ total = 1.0f/total;
+ else
+ total = 1.0f/M_PI;
+
+ t = patch->transfers;
+ t2 = all_transfers;
+ for (j=0 ; j<patch->numtransfers ; j++, t++, t2++)
+ {
+ t->transfer = t2->transfer*total;
+ t->patch = t2->patch;
+ }
+ if (patch->numtransfers > max_transfer)
+ {
+ max_transfer = patch->numtransfers;
+ }
+ }
+ else
+ {
+ // Error - patch has no transfers
+ // patch->totallight[2] = 255;
+ }
+
+ ThreadLock ();
+ total_transfer += patch->numtransfers;
+ ThreadUnlock ();
+}
+
+/*
+=============
+WriteWorld
+=============
+*/
+void WriteWorld (char *name, int iBump)
+{
+ unsigned j;
+ FileHandle_t out;
+ CPatch *patch;
+
+ out = g_pFileSystem->Open( name, "w" );
+ if (!out)
+ Error ("Couldn't open %s", name);
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ for (j=0; j<uiPatchCount; j++)
+ {
+ patch = &g_Patches.Element( j );
+
+ // skip parent patches
+ if (patch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ if( patch->clusterNumber == -1 )
+ {
+ Vector vGreen;
+ VectorClear( vGreen );
+ vGreen[1] = 256.0f;
+ WriteWinding( out, patch->winding, vGreen );
+ }
+ else
+ {
+ Vector light = patch->totallight.light[iBump] + patch->directlight;
+ WriteWinding( out, patch->winding, light );
+ if( bDumpNormals )
+ {
+ WriteNormal( out, patch->origin, patch->plane->normal, 15.0f, patch->plane->normal * 255.0f );
+ }
+ }
+ }
+
+ g_pFileSystem->Close( out );
+}
+
+void WriteRTEnv (char *name)
+{
+ FileHandle_t out;
+
+ out = g_pFileSystem->Open( name, "w" );
+ if (!out)
+ Error ("Couldn't open %s", name);
+
+ winding_t *triw = AllocWinding( 3 );
+ triw->numpoints = 3;
+
+ for( int i = 0; i < g_RtEnv.OptimizedTriangleList.Size(); i++ )
+ {
+ triw->p[0] = g_RtEnv.OptimizedTriangleList[i].Vertex( 0);
+ triw->p[1] = g_RtEnv.OptimizedTriangleList[i].Vertex( 1);
+ triw->p[2] = g_RtEnv.OptimizedTriangleList[i].Vertex( 2);
+ int id = g_RtEnv.OptimizedTriangleList[i].m_Data.m_GeometryData.m_nTriangleID;
+ Vector color(0, 0, 0);
+ if (id & TRACE_ID_OPAQUE) color.Init(0, 255, 0);
+ if (id & TRACE_ID_SKY) color.Init(0, 0, 255);
+ if (id & TRACE_ID_STATICPROP) color.Init(255, 0, 0);
+ WriteWinding(out, triw, color);
+ }
+ FreeWinding(triw);
+
+ g_pFileSystem->Close( out );
+}
+
+void WriteWinding (FileHandle_t out, winding_t *w, Vector& color )
+{
+ int i;
+
+ CmdLib_FPrintf (out, "%i\n", w->numpoints);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ CmdLib_FPrintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ color[ 0 ] / 256,
+ color[ 1 ] / 256,
+ color[ 2 ] / 256 );
+ }
+}
+
+
+void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir,
+ float length, Vector const &color )
+{
+ CmdLib_FPrintf( out, "2\n" );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ nPos.x, nPos.y, nPos.z,
+ color.x / 256, color.y / 256, color.z / 256 );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ nPos.x + ( nDir.x * length ),
+ nPos.y + ( nDir.y * length ),
+ nPos.z + ( nDir.z * length ),
+ color.x / 256, color.y / 256, color.z / 256 );
+}
+
+void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color )
+{
+ CmdLib_FPrintf( out, "2\n" );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ vecPos1.x, vecPos1.y, vecPos1.z,
+ color.x / 256, color.y / 256, color.z / 256 );
+ CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
+ vecPos2.x, vecPos2.y, vecPos2.z,
+ color.x / 256, color.y / 256, color.z / 256 );
+}
+
+void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result )
+{
+ FileHandle_t out;
+
+ out = g_pFileSystem->Open( pFileName, "a" );
+ if (!out)
+ Error ("Couldn't open %s", pFileName);
+
+ // Draws rays
+ for ( int i = 0; i < 4; ++i )
+ {
+ Vector vecOrigin = rays.origin.Vec(i);
+ Vector vecEnd = rays.direction.Vec(i);
+ VectorNormalize( vecEnd );
+ vecEnd *= SubFloat( result.HitDistance, i );
+ vecEnd += vecOrigin;
+ WriteLine( out, vecOrigin, vecEnd, Vector( 256, 0, 0 ) );
+ WriteNormal( out, vecEnd, result.surface_normal.Vec(i), 10.0f, Vector( 256, 265, 0 ) );
+ }
+
+ g_pFileSystem->Close( out );
+}
+
+
+/*
+=============
+CollectLight
+=============
+*/
+// patch's totallight += new light received to each patch
+// patch's emitlight = addlight (newly received light from GatherLight)
+// patch's addlight = 0
+// pull received light from children.
+void CollectLight( Vector& total )
+{
+ int i, j;
+ CPatch *patch;
+
+ VectorFill( total, 0 );
+
+ // process patches in reverse order so that children are processed before their parents
+ unsigned int uiPatchCount = g_Patches.Size();
+ for( i = uiPatchCount - 1; i >= 0; i-- )
+ {
+ patch = &g_Patches.Element( i );
+ int normalCount = patch->needsBumpmap ? NUM_BUMP_VECTS+1 : 1;
+ // sky's never collect light, it is just dropped
+ if (patch->sky)
+ {
+ VectorFill( emitlight[ i ], 0 );
+ }
+ else if ( patch->child1 == g_Patches.InvalidIndex() )
+ {
+ // This is a leaf node.
+ for ( j = 0; j < normalCount; j++ )
+ {
+ VectorAdd( patch->totallight.light[j], addlight[i].light[j], patch->totallight.light[j] );
+ }
+ VectorCopy( addlight[i].light[0], emitlight[i] );
+ VectorAdd( total, emitlight[i], total );
+ }
+ else
+ {
+ // This is an interior node.
+ // Pull received light from children.
+ float s1, s2;
+ CPatch *child1;
+ CPatch *child2;
+
+ child1 = &g_Patches[patch->child1];
+ child2 = &g_Patches[patch->child2];
+
+ // BUG: This doesn't do anything?
+ if ((int)patch->area != (int)(child1->area + child2->area))
+ s1 = 0;
+
+ s1 = child1->area / (child1->area + child2->area);
+ s2 = child2->area / (child1->area + child2->area);
+
+ // patch->totallight = s1 * child1->totallight + s2 * child2->totallight
+ for ( j = 0; j < normalCount; j++ )
+ {
+ VectorScale( child1->totallight.light[j], s1, patch->totallight.light[j] );
+ VectorMA( patch->totallight.light[j], s2, child2->totallight.light[j], patch->totallight.light[j] );
+ }
+
+ // patch->emitlight = s1 * child1->emitlight + s2 * child2->emitlight
+ VectorScale( emitlight[patch->child1], s1, emitlight[i] );
+ VectorMA( emitlight[i], s2, emitlight[patch->child2], emitlight[i] );
+ }
+ for ( j = 0; j < NUM_BUMP_VECTS+1; j++ )
+ {
+ VectorFill( addlight[ i ].light[j], 0 );
+ }
+ }
+}
+
+/*
+=============
+GatherLight
+
+Get light from other patches
+ Run multi-threaded
+=============
+*/
+
+#ifdef _WIN32
+#pragma warning (disable:4701)
+#endif
+
+extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
+ const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
+
+
+void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal )
+{
+ Vector vecTexU( pTexinfo->textureVecsTexelsPerWorldUnits[0][0], pTexinfo->textureVecsTexelsPerWorldUnits[0][1], pTexinfo->textureVecsTexelsPerWorldUnits[0][2] );
+ Vector vecTexV( pTexinfo->textureVecsTexelsPerWorldUnits[1][0], pTexinfo->textureVecsTexelsPerWorldUnits[1][1], pTexinfo->textureVecsTexelsPerWorldUnits[1][2] );
+ Vector vecLightU( pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ Vector vecLightV( pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][2] );
+
+ VectorNormalize( vecTexU );
+ VectorNormalize( vecTexV );
+ VectorNormalize( vecLightU );
+ VectorNormalize( vecLightV );
+
+ bool bDoConversion = false;
+ if ( fabs( vecTexU.Dot( vecLightU ) ) < 0.999f )
+ {
+ bDoConversion = true;
+ }
+
+ if ( fabs( vecTexV.Dot( vecLightV ) ) < 0.999f )
+ {
+ bDoConversion = true;
+ }
+
+ if ( bDoConversion )
+ {
+ matrix3x4_t matTex( vecTexU, vecTexV, vecNormal, vec3_origin );
+ matrix3x4_t matLight( vecLightU, vecLightV, vecNormal, vec3_origin );
+ matrix3x4_t matTmp;
+ ConcatTransforms ( matLight, matTex, matTmp );
+ MatrixGetColumn( matTmp, 0, vecU );
+ MatrixGetColumn( matTmp, 1, vecV );
+ MatrixGetColumn( matTmp, 2, vecNormal );
+
+ Assert( fabs( vecTexU.Dot( vecTexV ) ) <= 0.001f );
+ return;
+ }
+
+ vecU = vecTexU;
+ vecV = vecTexV;
+}
+
+void GatherLight (int threadnum, void *pUserData)
+{
+ int i, j, k;
+ transfer_t *trans;
+ int num;
+ CPatch *patch;
+ Vector sum, v;
+
+ while (1)
+ {
+ j = GetThreadWork ();
+ if (j == -1)
+ break;
+
+ patch = &g_Patches[j];
+
+ trans = patch->transfers;
+ num = patch->numtransfers;
+ if ( patch->needsBumpmap )
+ {
+ Vector delta;
+ Vector bumpSum[NUM_BUMP_VECTS+1];
+ Vector normals[NUM_BUMP_VECTS+1];
+
+ // Disps
+ bool bDisp = ( g_pFaces[patch->faceNumber].dispinfo != -1 );
+ if ( bDisp )
+ {
+ normals[0] = patch->normal;
+ texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
+ Vector vecTexU, vecTexV;
+ PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
+
+ // use facenormal along with the smooth normal to build the three bump map vectors
+ GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
+ }
+ else
+ {
+ GetPhongNormal( patch->faceNumber, patch->origin, normals[0] );
+
+ texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo];
+ // use facenormal along with the smooth normal to build the three bump map vectors
+ GetBumpNormals( pTexinfo->textureVecsTexelsPerWorldUnits[0],
+ pTexinfo->textureVecsTexelsPerWorldUnits[1], patch->normal,
+ normals[0], &normals[1] );
+ }
+
+ // force the base lightmap to use the flat normal instead of the phong normal
+ // FIXME: why does the patch not use the phong normal?
+ normals[0] = patch->normal;
+
+ for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
+ {
+ VectorFill( bumpSum[i], 0 );
+ }
+
+ float dot;
+ for (k=0 ; k<num ; k++, trans++)
+ {
+ CPatch *patch2 = &g_Patches[trans->patch];
+
+ // get vector to other patch
+ VectorSubtract (patch2->origin, patch->origin, delta);
+ VectorNormalize (delta);
+ // find light emitted from other patch
+ for(i=0; i<3; i++)
+ {
+ v[i] = emitlight[trans->patch][i] * patch2->reflectivity[i];
+ }
+ // remove normal already factored into transfer steradian
+ float scale = 1.0f / DotProduct (delta, patch->normal);
+ VectorScale( v, trans->transfer * scale, v );
+
+ Vector bumpTransfer;
+ for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
+ {
+ dot = DotProduct( delta, normals[i] );
+ if ( dot <= 0 )
+ {
+// Assert( i > 0 ); // if this hits, then the transfer shouldn't be here. It doesn't face the flat normal of this face!
+ continue;
+ }
+ bumpTransfer = v * dot;
+ VectorAdd( bumpSum[i], bumpTransfer, bumpSum[i] );
+ }
+ }
+ for ( i = 0; i < NUM_BUMP_VECTS+1; i++ )
+ {
+ VectorCopy( bumpSum[i], addlight[j].light[i] );
+ }
+ }
+ else
+ {
+ VectorFill( sum, 0 );
+ for (k=0 ; k<num ; k++, trans++)
+ {
+ for(i=0; i<3; i++)
+ {
+ v[i] = emitlight[trans->patch][i] * g_Patches[trans->patch].reflectivity[i];
+ }
+ VectorScale( v, trans->transfer, v );
+ VectorAdd( sum, v, sum );
+ }
+ VectorCopy( sum, addlight[j].light[0] );
+ }
+ }
+}
+
+#ifdef _WIN32
+#pragma warning (default:4701)
+#endif
+
+
+/*
+=============
+BounceLight
+=============
+*/
+void BounceLight (void)
+{
+ unsigned i;
+ Vector added;
+ char name[64];
+ qboolean bouncing = numbounce > 0;
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ for (i=0 ; i<uiPatchCount; i++)
+ {
+ // totallight has a copy of the direct lighting. Move it to the emitted light and zero it out (to integrate bounces only)
+ VectorCopy( g_Patches[i].totallight.light[0], emitlight[i] );
+
+ // NOTE: This means that only the bounced light is integrated into totallight!
+ VectorFill( g_Patches[i].totallight.light[0], 0 );
+ }
+
+#if 0
+ FileHandle_t dFp = g_pFileSystem->Open( "lightemit.txt", "w" );
+
+ unsigned int uiPatchCount = g_Patches.Size();
+ for (i=0 ; i<uiPatchCount; i++)
+ {
+ CmdLib_FPrintf( dFp, "Emit %d: %f %f %f\n", i, emitlight[i].x, emitlight[i].y, emitlight[i].z );
+ }
+
+ g_pFileSystem->Close( dFp );
+
+ for (i=0; i<num_patches ; i++)
+ {
+ Vector total;
+
+ VectorSubtract (g_Patches[i].maxs, g_Patches[i].mins, total);
+ Msg("%4d %4d %4d %4d (%d) %.0f", i, g_Patches[i].parent, g_Patches[i].child1, g_Patches[i].child2, g_Patches[i].samples, g_Patches[i].area );
+ Msg(" [%.0f %.0f %.0f]", total[0], total[1], total[2] );
+ if (g_Patches[i].child1 != g_Patches.InvalidIndex() )
+ {
+ Vector tmp;
+ VectorScale( g_Patches[i].totallight.light[0], g_Patches[i].area, tmp );
+
+ VectorMA( tmp, -g_Patches[g_Patches[i].child1].area, g_Patches[g_Patches[i].child1].totallight.light[0], tmp );
+ VectorMA( tmp, -g_Patches[g_Patches[i].child2].area, g_Patches[g_Patches[i].child2].totallight.light[0], tmp );
+ // Msg("%.0f ", VectorLength( tmp ) );
+ // Msg("%d ", g_Patches[i].samples - g_Patches[g_Patches[i].child1].samples - g_Patches[g_Patches[i].child2].samples );
+ // Msg("%d ", g_Patches[i].samples );
+ }
+ Msg("\n");
+ }
+#endif
+
+ i = 0;
+ while ( bouncing )
+ {
+ // transfer light from to the leaf patches from other patches via transfers
+ // this moves shooter->emitlight to receiver->addlight
+ unsigned int uiPatchCount = g_Patches.Size();
+ RunThreadsOn (uiPatchCount, true, GatherLight);
+ // move newly received light (addlight) to light to be sent out (emitlight)
+ // start at children and pull light up to parents
+ // light is always received to leaf patches
+ CollectLight( added );
+
+ qprintf ("\tBounce #%i added RGB(%.0f, %.0f, %.0f)\n", i+1, added[0], added[1], added[2] );
+
+ if ( i+1 == numbounce || (added[0] < 1.0 && added[1] < 1.0 && added[2] < 1.0) )
+ bouncing = false;
+
+ i++;
+ if ( g_bDumpPatches && !bouncing && i != 1)
+ {
+ sprintf (name, "bounce%i.txt", i);
+ WriteWorld (name, 0);
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Counts the number of clusters in a map with no visibility
+// Output : int
+//-----------------------------------------------------------------------------
+int CountClusters( void )
+{
+ int clusterCount = 0;
+
+ for ( int i = 0; i < numleafs; i++ )
+ {
+ if ( dleafs[i].cluster > clusterCount )
+ clusterCount = dleafs[i].cluster;
+ }
+
+ return clusterCount + 1;
+}
+
+
+/*
+=============
+RadWorld
+=============
+*/
+void RadWorld_Start()
+{
+ unsigned i;
+
+ if (luxeldensity < 1.0)
+ {
+ // Remember the old lightmap vectors.
+ float oldLightmapVecs[MAX_MAP_TEXINFO][2][4];
+ for (i = 0; i < texinfo.Count(); i++)
+ {
+ for( int j=0; j < 2; j++ )
+ {
+ for( int k=0; k < 3; k++ )
+ {
+ oldLightmapVecs[i][j][k] = texinfo[i].lightmapVecsLuxelsPerWorldUnits[j][k];
+ }
+ }
+ }
+
+ // rescale luxels to be no denser than "luxeldensity"
+ for (i = 0; i < texinfo.Count(); i++)
+ {
+ texinfo_t *tx = &texinfo[i];
+
+ for (int j = 0; j < 2; j++ )
+ {
+ Vector tmp( tx->lightmapVecsLuxelsPerWorldUnits[j][0], tx->lightmapVecsLuxelsPerWorldUnits[j][1], tx->lightmapVecsLuxelsPerWorldUnits[j][2] );
+ float scale = VectorNormalize( tmp );
+ // only rescale them if the current scale is "tighter" than the desired scale
+ // FIXME: since this writes out to the BSP file every run, once it's set high it can't be reset
+ // to a lower value.
+ if (fabs( scale ) > luxeldensity)
+ {
+ if (scale < 0)
+ {
+ scale = -luxeldensity;
+ }
+ else
+ {
+ scale = luxeldensity;
+ }
+ VectorScale( tmp, scale, tmp );
+ tx->lightmapVecsLuxelsPerWorldUnits[j][0] = tmp.x;
+ tx->lightmapVecsLuxelsPerWorldUnits[j][1] = tmp.y;
+ tx->lightmapVecsLuxelsPerWorldUnits[j][2] = tmp.z;
+ }
+ }
+ }
+
+ UpdateAllFaceLightmapExtents();
+ }
+
+ MakeParents (0, -1);
+
+ BuildClusterTable();
+
+ // turn each face into a single patch
+ MakePatches ();
+ PairEdges ();
+
+ // store the vertex normals calculated in PairEdges
+ // so that the can be written to the bsp file for
+ // use in the engine
+ SaveVertexNormals();
+
+ // subdivide patches to a maximum dimension
+ SubdividePatches ();
+
+ // add displacement faces to cluster table
+ AddDispsToClusterTable();
+
+ // create directlights out of patches and lights
+ CreateDirectLights ();
+
+ // set up sky cameras
+ ProcessSkyCameras();
+}
+
+
+// This function should fill in the indices into g_pFaces[] for the faces
+// with displacements that touch the specified leaf.
+void STUB_GetDisplacementsTouchingLeaf( int iLeaf, CUtlVector<int> &dispFaces )
+{
+}
+
+
+void BuildFacesVisibleToLights( bool bAllVisible )
+{
+ g_FacesVisibleToLights.SetSize( numfaces/8 + 1 );
+
+ if( bAllVisible )
+ {
+ memset( g_FacesVisibleToLights.Base(), 0xFF, g_FacesVisibleToLights.Count() );
+ return;
+ }
+
+ // First merge all the light PVSes.
+ CUtlVector<byte> aggregate;
+ aggregate.SetSize( (dvis->numclusters/8) + 1 );
+ memset( aggregate.Base(), 0, aggregate.Count() );
+
+ int nDWords = aggregate.Count() / 4;
+ int nBytes = aggregate.Count() - nDWords*4;
+
+ for( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
+ {
+ byte *pIn = dl->pvs;
+ byte *pOut = aggregate.Base();
+ for( int iDWord=0; iDWord < nDWords; iDWord++ )
+ {
+ *((unsigned long*)pOut) |= *((unsigned long*)pIn);
+ pIn += 4;
+ pOut += 4;
+ }
+
+ for( int iByte=0; iByte < nBytes; iByte++ )
+ {
+ *pOut |= *pIn;
+ ++pOut;
+ ++pIn;
+ }
+ }
+
+
+ // Now tag any faces that are visible to this monster PVS.
+ for( int iCluster=0; iCluster < dvis->numclusters; iCluster++ )
+ {
+ if( g_ClusterLeaves[iCluster].leafCount )
+ {
+ if( aggregate[iCluster>>3] & (1 << (iCluster & 7)) )
+ {
+ for ( int i = 0; i < g_ClusterLeaves[iCluster].leafCount; i++ )
+ {
+ int iLeaf = g_ClusterLeaves[iCluster].leafs[i];
+
+ // Tag all the faces.
+ int iFace;
+ for( iFace=0; iFace < dleafs[iLeaf].numleaffaces; iFace++ )
+ {
+ int index = dleafs[iLeaf].firstleafface + iFace;
+ index = dleaffaces[index];
+
+ assert( index < numfaces );
+ g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
+ }
+
+ // Fill in STUB_GetDisplacementsTouchingLeaf when it's available
+ // so displacements get relit.
+ CUtlVector<int> dispFaces;
+ STUB_GetDisplacementsTouchingLeaf( iLeaf, dispFaces );
+ for( iFace=0; iFace < dispFaces.Count(); iFace++ )
+ {
+ int index = dispFaces[iFace];
+ g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7));
+ }
+ }
+ }
+ }
+ }
+
+ // For stats.. figure out how many faces it's going to touch.
+ int nFacesToProcess = 0;
+ for( int i=0; i < numfaces; i++ )
+ {
+ if( g_FacesVisibleToLights[i>>3] & (1 << (i & 7)) )
+ ++nFacesToProcess;
+ }
+}
+
+
+
+void MakeAllScales (void)
+{
+ // determine visibility between patches
+ BuildVisMatrix ();
+
+ // release visibility matrix
+ FreeVisMatrix ();
+
+ Msg("transfers %d, max %d\n", total_transfer, max_transfer );
+
+ qprintf ("transfer lists: %5.1f megs\n"
+ , (float)total_transfer * sizeof(transfer_t) / (1024*1024));
+}
+
+
+// Helper function. This can be useful to visualize the world and faces and see which face
+// corresponds to which dface.
+#if 0
+ #include "iscratchpad3d.h"
+ void ScratchPad_DrawWorld()
+ {
+ IScratchPad3D *pPad = ScratchPad3D_Create();
+ pPad->SetAutoFlush( false );
+
+ for ( int i=0; i < numfaces; i++ )
+ {
+ dface_t *f = &g_pFaces[i];
+
+ // Draw the face's outline, then put text for its face index on it too.
+ CUtlVector<Vector> points;
+ for ( int iEdge = 0; iEdge < f->numedges; iEdge++ )
+ {
+ int v;
+ int se = dsurfedges[f->firstedge + iEdge];
+ if ( se < 0 )
+ v = dedges[-se].v[1];
+ else
+ v = dedges[se].v[0];
+
+ dvertex_t *dv = &dvertexes[v];
+ points.AddToTail( dv->point );
+ }
+
+ // Draw the outline.
+ Vector vCenter( 0, 0, 0 );
+ for ( iEdge=0; iEdge < points.Count(); iEdge++ )
+ {
+ pPad->DrawLine( CSPVert( points[iEdge] ), CSPVert( points[(iEdge+1)%points.Count()] ) );
+ vCenter += points[iEdge];
+ }
+ vCenter /= points.Count();
+
+ // Draw the text.
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "%d", i );
+
+ CTextParams params;
+
+ params.m_bCentered = true;
+ params.m_bOutline = true;
+ params.m_flLetterWidth = 2;
+ params.m_vColor.Init( 1, 0, 0 );
+
+ VectorAngles( dplanes[f->planenum].normal, params.m_vAngles );
+ params.m_bTwoSided = true;
+
+ params.m_vPos = vCenter;
+
+ pPad->DrawText( str, params );
+ }
+
+ pPad->Release();
+ }
+#endif
+
+
+bool RadWorld_Go()
+{
+ g_iCurFace = 0;
+
+ InitMacroTexture( source );
+
+ if( g_pIncremental )
+ {
+ g_pIncremental->PrepareForLighting();
+
+ // Cull out faces that aren't visible to any of the lights that we're updating with.
+ BuildFacesVisibleToLights( false );
+ }
+ else
+ {
+ // Mark all faces visible.. when not doing incremental lighting, it's highly
+ // likely that all faces are going to be touched by at least one light so don't
+ // waste time here.
+ BuildFacesVisibleToLights( true );
+ }
+
+ // build initial facelights
+ if (g_bUseMPI)
+ {
+ // RunThreadsOnIndividual (numfaces, true, BuildFacelights);
+ RunMPIBuildFacelights();
+ }
+ else
+ {
+ RunThreadsOnIndividual (numfaces, true, BuildFacelights);
+ }
+
+ // Was the process interrupted?
+ if( g_pIncremental && (g_iCurFace != numfaces) )
+ return false;
+
+ // Figure out the offset into lightmap data for each face.
+ PrecompLightmapOffsets();
+
+ // If we're doing incremental lighting, stop here.
+ if( g_pIncremental )
+ {
+ g_pIncremental->Finalize();
+ }
+ else
+ {
+ // free up the direct lights now that we have facelights
+ ExportDirectLightsToWorldLights();
+
+ if ( g_bDumpPatches )
+ {
+ for( int iBump = 0; iBump < 4; ++iBump )
+ {
+ char szName[64];
+ sprintf ( szName, "bounce0_%d.txt", iBump );
+ WriteWorld( szName, iBump );
+ }
+ }
+
+ if (numbounce > 0)
+ {
+ // allocate memory for emitlight/addlight
+ emitlight.SetSize( g_Patches.Size() );
+ memset( emitlight.Base(), 0, g_Patches.Size() * sizeof( Vector ) );
+ addlight.SetSize( g_Patches.Size() );
+ memset( addlight.Base(), 0, g_Patches.Size() * sizeof( bumplights_t ) );
+
+ MakeAllScales ();
+
+ // spread light around
+ BounceLight ();
+ }
+
+ //
+ // displacement surface luxel accumulation (make threaded!!!)
+ //
+ StaticDispMgr()->StartTimer( "Build Patch/Sample Hash Table(s)....." );
+ StaticDispMgr()->InsertSamplesDataIntoHashTable();
+ StaticDispMgr()->InsertPatchSampleDataIntoHashTable();
+ StaticDispMgr()->EndTimer();
+
+ // blend bounced light into direct light and save
+ VMPI_SetCurrentStage( "FinalLightFace" );
+ if ( !g_bUseMPI || g_bMPIMaster )
+ RunThreadsOnIndividual (numfaces, true, FinalLightFace);
+
+ // Distribute the lighting data to workers.
+ VMPI_DistributeLightData();
+
+ Msg("FinalLightFace Done\n"); fflush(stdout);
+ }
+
+ return true;
+}
+
+// declare the sample file pointer -- the whole debug print system should
+// be reworked at some point!!
+FileHandle_t pFileSamples[4][4];
+
+void LoadPhysicsDLL( void )
+{
+ PhysicsDLLPath( "VPHYSICS.DLL" );
+}
+
+
+void InitDumpPatchesFiles()
+{
+ for( int iStyle = 0; iStyle < 4; ++iStyle )
+ {
+ for ( int iBump = 0; iBump < 4; ++iBump )
+ {
+ char szFilename[MAX_PATH];
+ sprintf( szFilename, "samples_style%d_bump%d.txt", iStyle, iBump );
+ pFileSamples[iStyle][iBump] = g_pFileSystem->Open( szFilename, "w" );
+ if( !pFileSamples[iStyle][iBump] )
+ {
+ Error( "Can't open %s for -dump.\n", szFilename );
+ }
+ }
+ }
+}
+
+
+void VRAD_LoadBSP( char const *pFilename )
+{
+ ThreadSetDefault ();
+
+ g_flStartTime = Plat_FloatTime();
+
+ if( g_bLowPriority )
+ {
+ SetLowPriority();
+ }
+
+ strcpy( level_name, source );
+
+ // This must come after InitFileSystem because the file system pointer might change.
+ if ( g_bDumpPatches )
+ InitDumpPatchesFiles();
+
+ // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
+ // so we prepend qdir here.
+ strcpy( source, ExpandPath( source ) );
+
+ if ( !g_bUseMPI )
+ {
+ // Setup the logfile.
+ char logFile[512];
+ _snprintf( logFile, sizeof(logFile), "%s.log", source );
+ SetSpewFunctionLogFile( logFile );
+ }
+
+ LoadPhysicsDLL();
+
+ // Set the required global lights filename and try looking in qproject
+ strcpy( global_lights, "lights.rad" );
+ if ( !g_pFileSystem->FileExists( global_lights ) )
+ {
+ // Otherwise, try looking in the BIN directory from which we were run from
+ Msg( "Could not find lights.rad in %s.\nTrying VRAD BIN directory instead...\n",
+ global_lights );
+ GetModuleFileName( NULL, global_lights, sizeof( global_lights ) );
+ Q_ExtractFilePath( global_lights, global_lights, sizeof( global_lights ) );
+ strcat( global_lights, "lights.rad" );
+ }
+
+ // Set the optional level specific lights filename
+ strcpy( level_lights, source );
+
+ Q_DefaultExtension( level_lights, ".rad", sizeof( level_lights ) );
+ if ( !g_pFileSystem->FileExists( level_lights ) )
+ *level_lights = 0;
+
+ ReadLightFile(global_lights); // Required
+ if ( *designer_lights ) ReadLightFile(designer_lights); // Command-line
+ if ( *level_lights ) ReadLightFile(level_lights); // Optional & implied
+
+ strcpy(incrementfile, source);
+ Q_DefaultExtension(incrementfile, ".r0", sizeof(incrementfile));
+ Q_DefaultExtension(source, ".bsp", sizeof( source ));
+
+ GetPlatformMapPath( source, platformPath, 0, MAX_PATH );
+
+ Msg( "Loading %s\n", platformPath );
+ VMPI_SetCurrentStage( "LoadBSPFile" );
+ LoadBSPFile (platformPath);
+
+ // now, set whether or not static prop lighting is present
+ if (g_bStaticPropLighting)
+ g_LevelFlags |= g_bHDR? LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR : LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR;
+ else
+ {
+ g_LevelFlags &= ~( LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR | LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR );
+ }
+
+ // now, we need to set our face ptr depending upon hdr, and if hdr, init it
+ if (g_bHDR)
+ {
+ g_pFaces = dfaces_hdr;
+ if (numfaces_hdr==0)
+ {
+ numfaces_hdr = numfaces;
+ memcpy( dfaces_hdr, dfaces, numfaces*sizeof(dfaces[0]) );
+ }
+ }
+ else
+ {
+ g_pFaces = dfaces;
+ }
+
+
+ ParseEntities ();
+ ExtractBrushEntityShadowCasters();
+
+ StaticPropMgr()->Init();
+ StaticDispMgr()->Init();
+
+ if (!visdatasize)
+ {
+ Msg("No vis information, direct lighting only.\n");
+ numbounce = 0;
+ ambient[0] = ambient[1] = ambient[2] = 0.1f;
+ dvis->numclusters = CountClusters();
+ }
+
+ //
+ // patches and referencing data (ensure capacity)
+ //
+ // TODO: change the maxes to the amount from the bsp!!
+ //
+// g_Patches.EnsureCapacity( MAX_PATCHES );
+
+ g_FacePatches.SetSize( MAX_MAP_FACES );
+ faceParents.SetSize( MAX_MAP_FACES );
+ clusterChildren.SetSize( MAX_MAP_CLUSTERS );
+
+ int ndx;
+ for ( ndx = 0; ndx < MAX_MAP_FACES; ndx++ )
+ {
+ g_FacePatches[ndx] = g_FacePatches.InvalidIndex();
+ faceParents[ndx] = faceParents.InvalidIndex();
+ }
+
+ for ( ndx = 0; ndx < MAX_MAP_CLUSTERS; ndx++ )
+ {
+ clusterChildren[ndx] = clusterChildren.InvalidIndex();
+ }
+
+ // Setup ray tracer
+ AddBrushesForRayTrace();
+ StaticDispMgr()->AddPolysForRayTrace();
+ StaticPropMgr()->AddPolysForRayTrace();
+
+ // Dump raytracer for glview
+ if ( g_bDumpRtEnv )
+ WriteRTEnv("trace.txt");
+
+ // Build acceleration structure
+ printf ( "Setting up ray-trace acceleration structure... ");
+ float start = Plat_FloatTime();
+ g_RtEnv.SetupAccelerationStructure();
+ float end = Plat_FloatTime();
+ printf ( "Done (%.2f seconds)\n", end-start );
+
+#if 0 // To test only k-d build
+ exit(0);
+#endif
+
+ RadWorld_Start();
+
+ // Setup incremental lighting.
+ if( g_pIncremental )
+ {
+ if( !g_pIncremental->Init( source, incrementfile ) )
+ {
+ Error( "Unable to load incremental lighting file in %s.\n", incrementfile );
+ return;
+ }
+ }
+}
+
+
+void VRAD_ComputeOtherLighting()
+{
+ // Compute lighting for the bsp file
+ if ( !g_bNoDetailLighting )
+ {
+ ComputeDetailPropLighting( THREADINDEX_MAIN );
+ }
+
+ ComputePerLeafAmbientLighting();
+
+ // bake the static props high quality vertex lighting into the bsp
+ if ( !do_fast && g_bStaticPropLighting )
+ {
+ StaticPropMgr()->ComputeLighting( THREADINDEX_MAIN );
+ }
+}
+
+extern void CloseDispLuxels();
+
+void VRAD_Finish()
+{
+ Msg( "Ready to Finish\n" );
+ fflush( stdout );
+
+ if ( verbose )
+ {
+ PrintBSPFileSizes();
+ }
+
+ Msg( "Writing %s\n", platformPath );
+ VMPI_SetCurrentStage( "WriteBSPFile" );
+ WriteBSPFile(platformPath);
+
+ if ( g_bDumpPatches )
+ {
+ for ( int iStyle = 0; iStyle < 4; ++iStyle )
+ {
+ for ( int iBump = 0; iBump < 4; ++iBump )
+ {
+ g_pFileSystem->Close( pFileSamples[iStyle][iBump] );
+ }
+ }
+ }
+
+ CloseDispLuxels();
+
+ StaticPropMgr()->Shutdown();
+
+ double end = Plat_FloatTime();
+
+ char str[512];
+ GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) );
+ Msg( "%s elapsed\n", str );
+
+ ReleasePakFileLumps();
+}
+
+
+// Run startup code like initialize mathlib (called from main() and from the
+// WorldCraft interface into vrad).
+void VRAD_Init()
+{
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+ InstallAllocationFunctions();
+ InstallSpewFunction();
+}
+
+
+int ParseCommandLine( int argc, char **argv, bool *onlydetail )
+{
+ *onlydetail = false;
+
+ // default to LDR
+ SetHDRMode( false );
+ int i;
+ for( i=1 ; i<argc ; i++ )
+ {
+ if ( !Q_stricmp( argv[i], "-StaticPropLighting" ) )
+ {
+ g_bStaticPropLighting = true;
+ }
+ else if ( !stricmp( argv[i], "-StaticPropNormals" ) )
+ {
+ g_bShowStaticPropNormals = true;
+ }
+ else if ( !stricmp( argv[i], "-OnlyStaticProps" ) )
+ {
+ g_bOnlyStaticProps = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-StaticPropPolys" ) )
+ {
+ g_bStaticPropPolys = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-nossprops" ) )
+ {
+ g_bDisablePropSelfShadowing = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-textureshadows" ) )
+ {
+ g_bTextureShadows = true;
+ }
+ else if ( !strcmp(argv[i], "-dump") )
+ {
+ g_bDumpPatches = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-nodetaillight" ) )
+ {
+ g_bNoDetailLighting = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-rederrors" ) )
+ {
+ bRed2Black = false;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumpnormals" ) )
+ {
+ bDumpNormals = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-dumptrace" ) )
+ {
+ g_bDumpRtEnv = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-LargeDispSampleRadius" ) )
+ {
+ g_bLargeDispSampleRadius = true;
+ }
+ else if (!Q_stricmp(argv[i],"-bounce"))
+ {
+ if ( ++i < argc )
+ {
+ numbounce = atoi (argv[i]);
+ if ( numbounce < 0 )
+ {
+ Warning("Error: expected non-negative value after '-bounce'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning("Error: expected a value after '-bounce'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-verbose") || !Q_stricmp(argv[i],"-v"))
+ {
+ verbose = true;
+ }
+ else if (!Q_stricmp(argv[i],"-threads"))
+ {
+ if ( ++i < argc )
+ {
+ numthreads = atoi (argv[i]);
+ if ( numthreads <= 0 )
+ {
+ Warning("Error: expected positive value after '-threads'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning("Error: expected a value after '-threads'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp(argv[i], "-lights" ) )
+ {
+ if ( ++i < argc && *argv[i] )
+ {
+ strcpy( designer_lights, argv[i] );
+ }
+ else
+ {
+ Warning("Error: expected a filepath after '-lights'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-noextra"))
+ {
+ do_extra = false;
+ }
+ else if (!Q_stricmp(argv[i],"-debugextra"))
+ {
+ debug_extra = true;
+ }
+ else if ( !Q_stricmp(argv[i], "-fastambient") )
+ {
+ g_bFastAmbient = true;
+ }
+ else if (!Q_stricmp(argv[i],"-fast"))
+ {
+ do_fast = true;
+ }
+ else if (!Q_stricmp(argv[i],"-noskyboxrecurse"))
+ {
+ g_bNoSkyRecurse = true;
+ }
+ else if (!Q_stricmp(argv[i],"-final"))
+ {
+ g_flSkySampleScale = 16.0;
+ }
+ else if (!Q_stricmp(argv[i],"-extrasky"))
+ {
+ if ( ++i < argc && *argv[i] )
+ {
+ g_flSkySampleScale = atof( argv[i] );
+ }
+ else
+ {
+ Warning("Error: expected a scale factor after '-extrasky'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-centersamples"))
+ {
+ do_centersamples = true;
+ }
+ else if (!Q_stricmp(argv[i],"-smooth"))
+ {
+ if ( ++i < argc )
+ {
+ smoothing_threshold = (float)cos(atof(argv[i])*(M_PI/180.0));
+ }
+ else
+ {
+ Warning("Error: expected an angle after '-smooth'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-dlightmap"))
+ {
+ dlight_map = 1;
+ }
+ else if (!Q_stricmp(argv[i],"-luxeldensity"))
+ {
+ if ( ++i < argc )
+ {
+ luxeldensity = (float)atof (argv[i]);
+ if (luxeldensity > 1.0)
+ luxeldensity = 1.0 / luxeldensity;
+ }
+ else
+ {
+ Warning("Error: expected a value after '-luxeldensity'\n" );
+ return 1;
+ }
+ }
+ else if( !Q_stricmp( argv[i], "-low" ) )
+ {
+ g_bLowPriority = true;
+ }
+ else if( !Q_stricmp( argv[i], "-loghash" ) )
+ {
+ g_bLogHashData = true;
+ }
+ else if( !Q_stricmp( argv[i], "-onlydetail" ) )
+ {
+ *onlydetail = true;
+ }
+ else if (!Q_stricmp(argv[i],"-softsun"))
+ {
+ if ( ++i < argc )
+ {
+ g_SunAngularExtent=atof(argv[i]);
+ g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent);
+ printf("sun extent=%f\n",g_SunAngularExtent);
+ }
+ else
+ {
+ Warning("Error: expected an angular extent value (0..180) '-softsun'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp( argv[i], "-maxdispsamplesize" ) )
+ {
+ if ( ++i < argc )
+ {
+ g_flMaxDispSampleSize = ( float )atof( argv[i] );
+ }
+ else
+ {
+ Warning( "Error: expected a sample size after '-maxdispsamplesize'\n" );
+ return 1;
+ }
+ }
+ else if ( stricmp( argv[i], "-StopOnExit" ) == 0 )
+ {
+ g_bStopOnExit = true;
+ }
+ else if ( stricmp( argv[i], "-steam" ) == 0 )
+ {
+ }
+ else if ( stricmp( argv[i], "-allowdebug" ) == 0 )
+ {
+ // Don't need to do anything, just don't error out.
+ }
+ else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
+ {
+ }
+ else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
+ {
+ ++i;
+ }
+ else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
+ {
+ EnableFullMinidumps( true );
+ }
+ else if ( !Q_stricmp( argv[i], "-hdr" ) )
+ {
+ SetHDRMode( true );
+ }
+ else if ( !Q_stricmp( argv[i], "-ldr" ) )
+ {
+ SetHDRMode( false );
+ }
+ else if (!Q_stricmp(argv[i],"-maxchop"))
+ {
+ if ( ++i < argc )
+ {
+ maxchop = (float)atof (argv[i]);
+ if ( maxchop < 1 )
+ {
+ Warning("Error: expected positive value after '-maxchop'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning("Error: expected a value after '-maxchop'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-chop"))
+ {
+ if ( ++i < argc )
+ {
+ minchop = (float)atof (argv[i]);
+ if ( minchop < 1 )
+ {
+ Warning("Error: expected positive value after '-chop'\n" );
+ return 1;
+ }
+ minchop = min( minchop, maxchop );
+ }
+ else
+ {
+ Warning("Error: expected a value after '-chop'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp( argv[i], "-dispchop" ) )
+ {
+ if ( ++i < argc )
+ {
+ dispchop = ( float )atof( argv[i] );
+ if ( dispchop < 1.0f )
+ {
+ Warning( "Error: expected positive value after '-dipschop'\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning( "Error: expected a value after '-dispchop'\n" );
+ return 1;
+ }
+ }
+ else if ( !Q_stricmp( argv[i], "-disppatchradius" ) )
+ {
+ if ( ++i < argc )
+ {
+ g_MaxDispPatchRadius = ( float )atof( argv[i] );
+ if ( g_MaxDispPatchRadius < 10.0f )
+ {
+ Warning( "Error: g_MaxDispPatchRadius < 10.0\n" );
+ return 1;
+ }
+ }
+ else
+ {
+ Warning( "Error: expected a value after '-disppatchradius'\n" );
+ return 1;
+ }
+ }
+
+#if ALLOWDEBUGOPTIONS
+ else if (!Q_stricmp(argv[i],"-scale"))
+ {
+ if ( ++i < argc )
+ {
+ lightscale = (float)atof (argv[i]);
+ }
+ else
+ {
+ Warning("Error: expected a value after '-scale'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-ambient"))
+ {
+ if ( i+3 < argc )
+ {
+ ambient[0] = (float)atof (argv[++i]) * 128;
+ ambient[1] = (float)atof (argv[++i]) * 128;
+ ambient[2] = (float)atof (argv[++i]) * 128;
+ }
+ else
+ {
+ Warning("Error: expected three color values after '-ambient'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-dlight"))
+ {
+ if ( ++i < argc )
+ {
+ dlight_threshold = (float)atof (argv[i]);
+ }
+ else
+ {
+ Warning("Error: expected a value after '-dlight'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-sky"))
+ {
+ if ( ++i < argc )
+ {
+ indirect_sun = (float)atof (argv[i]);
+ }
+ else
+ {
+ Warning("Error: expected a value after '-sky'\n" );
+ return 1;
+ }
+ }
+ else if (!Q_stricmp(argv[i],"-notexscale"))
+ {
+ texscale = false;
+ }
+ else if (!Q_stricmp(argv[i],"-coring"))
+ {
+ if ( ++i < argc )
+ {
+ coring = (float)atof( argv[i] );
+ }
+ else
+ {
+ Warning("Error: expected a light threshold after '-coring'\n" );
+ return 1;
+ }
+ }
+#endif
+ // NOTE: the -mpi checks must come last here because they allow the previous argument
+ // to be -mpi as well. If it game before something else like -game, then if the previous
+ // argument was -mpi and the current argument was something valid like -game, it would skip it.
+ else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
+ {
+ if ( stricmp( argv[i], "-mpi" ) == 0 )
+ g_bUseMPI = true;
+
+ // Any other args that start with -mpi are ok too.
+ if ( i == argc - 1 && V_stricmp( argv[i], "-mpi_ListParams" ) != 0 )
+ break;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return i;
+}
+
+
+void PrintCommandLine( int argc, char **argv )
+{
+ Warning( "Command line: " );
+ for ( int z=0; z < argc; z++ )
+ {
+ Warning( "\"%s\" ", argv[z] );
+ }
+ Warning( "\n\n" );
+}
+
+
+void PrintUsage( int argc, char **argv )
+{
+ PrintCommandLine( argc, argv );
+
+ Warning(
+ "usage : vrad [options...] bspfile\n"
+ "example: vrad c:\\hl2\\hl2\\maps\\test\n"
+ "\n"
+ "Common options:\n"
+ "\n"
+ " -v (or -verbose): Turn on verbose output (also shows more command\n"
+ " -bounce # : Set max number of bounces (default: 100).\n"
+ " -fast : Quick and dirty lighting.\n"
+ " -fastambient : Per-leaf ambient sampling is lower quality to save compute time.\n"
+ " -final : High quality processing. equivalent to -extrasky 16.\n"
+ " -extrasky n : trace N times as many rays for indirect light and sky ambient.\n"
+ " -low : Run as an idle-priority process.\n"
+ " -mpi : Use VMPI to distribute computations.\n"
+ " -rederror : Show errors in red.\n"
+ "\n"
+ " -vproject <directory> : Override the VPROJECT environment variable.\n"
+ " -game <directory> : Same as -vproject.\n"
+ "\n"
+ "Other options:\n"
+ " -novconfig : Don't bring up graphical UI on vproject errors.\n"
+ " -dump : Write debugging .txt files.\n"
+ " -dumpnormals : Write normals to debug files.\n"
+ " -dumptrace : Write ray-tracing environment to debug files.\n"
+ " -threads : Control the number of threads vbsp uses (defaults to the #\n"
+ " or processors on your machine).\n"
+ " -lights <file> : Load a lights file in addition to lights.rad and the\n"
+ " level lights file.\n"
+ " -noextra : Disable supersampling.\n"
+ " -debugextra : Places debugging data in lightmaps to visualize\n"
+ " supersampling.\n"
+ " -smooth # : Set the threshold for smoothing groups, in degrees\n"
+ " (default 45).\n"
+ " -dlightmap : Force direct lighting into different lightmap than\n"
+ " radiosity.\n"
+ " -stoponexit : Wait for a keypress on exit.\n"
+ " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
+ " -nodetaillight : Don't light detail props.\n"
+ " -centersamples : Move sample centers.\n"
+ " -luxeldensity # : Rescale all luxels by the specified amount (default: 1.0).\n"
+ " The number specified must be less than 1.0 or it will be\n"
+ " ignored.\n"
+ " -loghash : Log the sample hash table to samplehash.txt.\n"
+ " -onlydetail : Only light detail props and per-leaf lighting.\n"
+ " -maxdispsamplesize #: Set max displacement sample size (default: 512).\n"
+ " -softsun <n> : Treat the sun as an area light source of size <n> degrees."
+ " Produces soft shadows.\n"
+ " Recommended values are between 0 and 5. Default is 0.\n"
+ " -FullMinidumps : Write large minidumps on crash.\n"
+ " -chop : Smallest number of luxel widths for a bounce patch, used on edges\n"
+ " -maxchop : Coarsest allowed number of luxel widths for a patch, used in face interiors\n"
+ "\n"
+ " -LargeDispSampleRadius: This can be used if there are splotches of bounced light\n"
+ " on terrain. The compile will take longer, but it will gather\n"
+ " light across a wider area.\n"
+ " -StaticPropLighting : generate backed static prop vertex lighting\n"
+ " -StaticPropPolys : Perform shadow tests of static props at polygon precision\n"
+ " -OnlyStaticProps : Only perform direct static prop lighting (vrad debug option)\n"
+ " -StaticPropNormals : when lighting static props, just show their normal vector\n"
+ " -textureshadows : Allows texture alpha channels to block light - rays intersecting alpha surfaces will sample the texture\n"
+ " -noskyboxrecurse : Turn off recursion into 3d skybox (skybox shadows on world)\n"
+ " -nossprops : Globally disable self-shadowing on static props\n"
+ "\n"
+#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
+ );
+#else
+ " -mpi_ListParams : Show a list of VMPI parameters.\n"
+ "\n"
+ );
+
+ // Show VMPI parameters?
+ for ( int i=1; i < argc; i++ )
+ {
+ if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
+ {
+ Warning( "VMPI-specific options:\n\n" );
+
+ bool bIsSDKMode = VMPI_IsSDKMode();
+ for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
+ {
+ if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
+ continue;
+
+ Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
+ Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
+ Warning( "\n\n" );
+ }
+ break;
+ }
+ }
+#endif
+}
+
+
+int RunVRAD( int argc, char **argv )
+{
+#if defined(_MSC_VER) && ( _MSC_VER >= 1310 )
+ Msg("Valve Software - vrad.exe SSE (" __DATE__ ")\n" );
+#else
+ Msg("Valve Software - vrad.exe (" __DATE__ ")\n" );
+#endif
+
+ Msg("\n Valve Radiosity Simulator \n");
+
+ verbose = true; // Originally FALSE
+
+ bool onlydetail;
+ int i = ParseCommandLine( argc, argv, &onlydetail );
+ if (i != argc - 1)
+ {
+ PrintUsage( argc, argv );
+ DeleteCmdLine( argc, argv );
+ CmdLib_Exit( 1 );
+ }
+
+ VRAD_LoadBSP( argv[i] );
+
+ if ( (! onlydetail) && (! g_bOnlyStaticProps ) )
+ {
+ RadWorld_Go();
+ }
+
+ VRAD_ComputeOtherLighting();
+
+ VRAD_Finish();
+
+ VMPI_SetCurrentStage( "master done" );
+
+ DeleteCmdLine( argc, argv );
+ CmdLib_Cleanup();
+ return 0;
+}
+
+
+int VRAD_Main(int argc, char **argv)
+{
+ g_pFileSystem = NULL; // Safeguard against using it before it's properly initialized.
+
+ VRAD_Init();
+
+ // This must come first.
+ VRAD_SetupMPI( argc, argv );
+
+ // Initialize the filesystem, so additional commandline options can be loaded
+ Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) );
+ CmdLib_InitFileSystem( argv[ argc - 1 ] );
+ Q_FileBase( source, source, sizeof( source ) );
+
+#if !defined( _DEBUG )
+ if ( g_bUseMPI && !g_bMPIMaster )
+ {
+ SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
+ }
+ else
+#endif
+ {
+ LoadCmdLineFromFile( argc, argv, source, "vrad" ); // Don't do this if we're a VMPI worker..
+ SetupDefaultToolsMinidumpHandler();
+ }
+
+ return RunVRAD( argc, argv );
+}
+
+
+
+
+
diff --git a/mp/src/utils/vrad/vrad.h b/mp/src/utils/vrad/vrad.h new file mode 100644 index 00000000..b9dc0a3f --- /dev/null +++ b/mp/src/utils/vrad/vrad.h @@ -0,0 +1,610 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VRAD_H
+#define VRAD_H
+#pragma once
+
+
+#include "commonmacros.h"
+#include "worldsize.h"
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "bsplib.h"
+#include "polylib.h"
+#include "threads.h"
+#include "builddisp.h"
+#include "VRAD_DispColl.h"
+#include "UtlMemory.h"
+#include "UtlHash.h"
+#include "utlvector.h"
+#include "iincremental.h"
+#include "raytrace.h"
+
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#pragma warning(disable: 4142 4028)
+#include <io.h>
+#pragma warning(default: 4142 4028)
+
+#include <fcntl.h>
+#include <direct.h>
+#include <ctype.h>
+
+
+// Can remove these options if they don't generate problems.
+//#define SAMPLEHASH_USE_AREA_PATCHES // Add patches to sample hash based on their AABB instead of as a single point.
+#define SAMPLEHASH_QUERY_ONCE // Big optimization - causes way less sample hash queries.
+
+extern float dispchop; // "-dispchop" tightest number of luxel widths for a patch, used on edges
+extern float g_MaxDispPatchRadius;
+
+//-----------------------------------------------------------------------------
+// forward declarations
+//-----------------------------------------------------------------------------
+
+struct Ray_t;
+
+#define TRANSFER_EPSILON 0.0000001
+
+struct directlight_t
+{
+ int index;
+
+ directlight_t *next;
+ dworldlight_t light;
+
+ byte *pvs; // accumulated domain of the light
+ int facenum; // domain of attached lights
+ int texdata; // texture source of traced lights
+
+ Vector snormal;
+ Vector tnormal;
+ float sscale;
+ float tscale;
+ float soffset;
+ float toffset;
+
+ int dorecalc; // position, vector, spot angle, etc.
+ IncrementalLightID m_IncrementalID;
+
+ // hard-falloff lights (lights that fade to an actual zero). between m_flStartFadeDistance and
+ // m_flEndFadeDistance, a smoothstep to zero will be done, so that the light goes to zero at
+ // the end.
+ float m_flStartFadeDistance;
+ float m_flEndFadeDistance;
+ float m_flCapDist; // max distance to feed in
+
+ directlight_t(void)
+ {
+ m_flEndFadeDistance = -1.0; // end<start indicates not set
+ m_flStartFadeDistance= 0.0;
+ m_flCapDist = 1.0e22;
+
+ }
+};
+
+struct bumplights_t
+{
+ Vector light[NUM_BUMP_VECTS+1];
+};
+
+
+struct transfer_t
+{
+ int patch;
+ float transfer;
+};
+
+
+struct LightingValue_t
+{
+ Vector m_vecLighting;
+ float m_flDirectSunAmount;
+
+ FORCEINLINE bool IsValid( void ) const
+ {
+ return ( m_vecLighting.x >= 0 &&
+ m_vecLighting.y >= 0 &&
+ m_vecLighting.z >= 0 &&
+ m_vecLighting.x < 1e10 &&
+ m_vecLighting.y < 1e10 &&
+ m_vecLighting.z < 1e10 );
+ }
+
+ FORCEINLINE void Zero( void )
+ {
+ m_vecLighting.Init( 0, 0, 0 );
+ m_flDirectSunAmount = 0.0;
+ }
+
+ FORCEINLINE void Scale( float m_flScale )
+ {
+ m_vecLighting *= m_flScale;
+ m_flDirectSunAmount *= m_flScale;
+ }
+
+ FORCEINLINE void AddWeighted( LightingValue_t const &src, float flWeight )
+ {
+ m_vecLighting += flWeight * src.m_vecLighting;
+ m_flDirectSunAmount += flWeight * src.m_flDirectSunAmount;
+ }
+
+ FORCEINLINE void AddWeighted( Vector const &src, float flWeight )
+ {
+ m_vecLighting += flWeight * src;
+ }
+
+ FORCEINLINE float Intensity( void ) const
+ {
+ return m_vecLighting.x + m_vecLighting.y + m_vecLighting.z;
+ }
+
+ FORCEINLINE void AddLight( float flAmount, Vector const &vecColor, float flSunAmount = 0.0 )
+ {
+ VectorMA( m_vecLighting, flAmount, vecColor, m_vecLighting );
+ m_flDirectSunAmount += flSunAmount;
+ Assert( this->IsValid() );
+ }
+
+
+ FORCEINLINE void AddLight( LightingValue_t const &src )
+ {
+ m_vecLighting += src.m_vecLighting;
+ m_flDirectSunAmount += src.m_flDirectSunAmount;
+ Assert( this->IsValid() );
+ }
+
+ FORCEINLINE void Init( float x, float y, float z )
+ {
+ m_vecLighting.Init( x, y, z );
+ m_flDirectSunAmount = 0.0;
+ }
+
+
+};
+
+
+#define MAX_PATCHES (4*65536)
+
+struct CPatch
+{
+ winding_t *winding;
+ Vector mins, maxs, face_mins, face_maxs;
+
+ Vector origin; // adjusted off face by face normal
+
+ dplane_t *plane; // plane (corrected for facing)
+
+ unsigned short m_IterationKey; // Used to prevent touching the same patch multiple times in the same query.
+ // See IncrementPatchIterationKey().
+
+ // these are packed into one dword
+ unsigned int normalMajorAxis : 2; // the major axis of base face normal
+ unsigned int sky : 1;
+ unsigned int needsBumpmap : 1;
+ unsigned int pad : 28;
+
+ Vector normal; // adjusted for phong shading
+
+ float planeDist; // Fixes up patch planes for brush models with an origin brush
+
+ float chop; // smallest acceptable width of patch face
+ float luxscale; // average luxels per world coord
+ float scale[2]; // Scaling of texture in s & t
+
+ bumplights_t totallight; // accumulated by radiosity
+ // does NOT include light
+ // accounted for by direct lighting
+ Vector baselight; // emissivity only
+ float basearea; // surface per area per baselight instance
+
+ Vector directlight; // direct light value
+ float area;
+
+ Vector reflectivity; // Average RGB of texture, modified by material type.
+
+ Vector samplelight;
+ float samplearea; // for averaging direct light
+ int faceNumber;
+ int clusterNumber;
+
+ int parent; // patch index of parent
+ int child1; // patch index for children
+ int child2;
+
+ int ndxNext; // next patch index in face
+ int ndxNextParent; // next parent patch index in face
+ int ndxNextClusterChild; // next terminal child index in cluster
+// struct patch_s *next; // next in face
+// struct patch_s *nextparent; // next in face
+// struct patch_s *nextclusterchild; // next terminal child in cluster
+
+ int numtransfers;
+ transfer_t *transfers;
+
+ short indices[3]; // displacement use these for subdivision
+};
+
+
+extern CUtlVector<CPatch> g_Patches;
+extern CUtlVector<int> g_FacePatches; // constains all patches, children first
+extern CUtlVector<int> faceParents; // contains only root patches, use next parent to iterate
+extern CUtlVector<int> clusterChildren;
+
+
+struct sky_camera_t
+{
+ Vector origin;
+ float world_to_sky;
+ float sky_to_world;
+ int area;
+};
+
+extern int num_sky_cameras;
+extern sky_camera_t sky_cameras[MAX_MAP_AREAS];
+extern int area_sky_cameras[MAX_MAP_AREAS];
+void ProcessSkyCameras();
+
+extern entity_t *face_entity[MAX_MAP_FACES];
+extern Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels
+extern Vector face_centroids[MAX_MAP_EDGES];
+extern int leafparents[MAX_MAP_LEAFS];
+extern int nodeparents[MAX_MAP_NODES];
+extern float lightscale;
+extern float dlight_threshold;
+extern float coring;
+extern qboolean g_bDumpPatches;
+extern bool bRed2Black;
+extern bool g_bNoSkyRecurse;
+extern bool bDumpNormals;
+extern bool g_bFastAmbient;
+extern float maxchop;
+extern FileHandle_t pFileSamples[4][4];
+extern qboolean g_bLowPriority;
+extern qboolean do_fast;
+extern bool g_bInterrupt; // Was used with background lighting in WC. Tells VRAD to stop lighting.
+extern IIncremental *g_pIncremental; // null if not doing incremental lighting
+
+extern float g_flSkySampleScale; // extra sampling factor for indirect light
+
+extern bool g_bLargeDispSampleRadius;
+extern bool g_bStaticPropPolys;
+extern bool g_bTextureShadows;
+extern bool g_bShowStaticPropNormals;
+extern bool g_bDisablePropSelfShadowing;
+
+extern CUtlVector<char const *> g_NonShadowCastingMaterialStrings;
+extern void ForceTextureShadowsOnModel( const char *pModelName );
+extern bool IsModelTextureShadowsForced( const char *pModelName );
+
+// Raytracing
+
+#define TRACE_ID_SKY 0x01000000 // sky face ray blocker
+#define TRACE_ID_OPAQUE 0x02000000 // everyday light blocking face
+#define TRACE_ID_STATICPROP 0x04000000 // static prop - lower bits are prop ID
+extern RayTracingEnvironment g_RtEnv;
+
+#include "mpivrad.h"
+
+void MakeShadowSplits (void);
+
+//==============================================
+
+void BuildVisMatrix (void);
+void BuildClusterTable( void );
+void AddDispsToClusterTable( void );
+void FreeVisMatrix (void);
+// qboolean CheckVisBit (unsigned int p1, unsigned int p2);
+void TouchVMFFile (void);
+
+//==============================================
+
+extern qboolean do_extra;
+extern qboolean do_fast;
+extern qboolean do_centersamples;
+extern int extrapasses;
+extern Vector ambient;
+extern float maxlight;
+extern unsigned numbounce;
+extern qboolean g_bLogHashData;
+extern bool debug_extra;
+extern directlight_t *activelights;
+extern directlight_t *freelights;
+
+// because of hdr having two face lumps (light styles can cause them to be different, among other
+// things), we need to always access (r/w) face data though this pointer
+extern dface_t *g_pFaces;
+
+
+extern bool g_bMPIProps;
+
+extern byte nodehit[MAX_MAP_NODES];
+extern float gamma;
+extern float indirect_sun;
+extern float smoothing_threshold;
+extern int dlight_map;
+
+extern float g_flMaxDispSampleSize;
+extern float g_SunAngularExtent;
+
+extern char source[MAX_PATH];
+
+// Used by incremental lighting to trivial-reject faces.
+// There is a bit in here for each face telling whether or not any of the
+// active lights can see the face.
+extern CUtlVector<byte> g_FacesVisibleToLights;
+
+void MakeTnodes (dmodel_t *bm);
+void PairEdges (void);
+
+void SaveVertexNormals( void );
+
+qboolean IsIncremental(char *filename);
+int SaveIncremental(char *filename);
+int PartialHead (void);
+void BuildFacelights (int facenum, int threadnum);
+void PrecompLightmapOffsets();
+void FinalLightFace (int threadnum, int facenum);
+void PvsForOrigin (Vector& org, byte *pvs);
+void ConvertRGBExp32ToRGBA8888( const ColorRGBExp32 *pSrc, unsigned char *pDst );
+
+inline byte PVSCheck( const byte *pvs, int iCluster )
+{
+ if ( iCluster >= 0 )
+ {
+ return pvs[iCluster >> 3] & ( 1 << ( iCluster & 7 ) );
+ }
+ else
+ {
+ // PointInLeaf still returns -1 for valid points sometimes and rather than
+ // have black samples, we assume the sample is in the PVS.
+ return 1;
+ }
+}
+
+// outputs 1 in fractionVisible if no occlusion, 0 if full occlusion, and in-between values
+void TestLine( FourVectors const& start, FourVectors const& stop, fltx4 *pFractionVisible, int static_prop_index_to_ignore=-1);
+
+// returns 1 if the ray sees the sky, 0 if it doesn't, and in-between values for partial coverage
+void TestLine_DoesHitSky( FourVectors const& start, FourVectors const& stop,
+ fltx4 *pFractionVisible, bool canRecurse = true, int static_prop_to_skip=-1, bool bDoDebug = false );
+
+// converts any marked brush entities to triangles for shadow casting
+void ExtractBrushEntityShadowCasters ( void );
+void AddBrushesForRayTrace ( void );
+
+void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity );
+void CreateDirectLights (void);
+void GetPhongNormal( int facenum, Vector const& spot, Vector& phongnormal );
+int LightForString( char *pLight, Vector& intensity );
+void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers );
+void MakeScales( int ndxPatch, transfer_t *all_transfers );
+
+// Run startup code like initialize mathlib.
+void VRAD_Init();
+
+// Load the BSP file and prepare to do the lighting.
+// This is called after any command-line parameters have been set.
+void VRAD_LoadBSP( char const *pFilename );
+
+int VRAD_Main(int argc, char **argv);
+
+// This performs an actual lighting pass.
+// Returns true if the process was interrupted (with g_bInterrupt).
+bool RadWorld_Go();
+
+dleaf_t *PointInLeaf (Vector const& point);
+int ClusterFromPoint( Vector const& point );
+winding_t *WindingFromFace (dface_t *f, Vector& origin );
+
+void WriteWinding (FileHandle_t out, winding_t *w, Vector& color );
+void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir,
+ float length, Vector const &color );
+void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color );
+void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result );
+
+#ifdef STATIC_FOG
+qboolean IsFog( dface_t * f );
+#endif
+
+#define CONTENTS_EMPTY 0
+#define TEX_SPECIAL (SURF_SKY|SURF_NOLIGHT)
+
+//=============================================================================
+
+// trace.cpp
+
+bool AddDispCollTreesToWorld( void );
+int PointLeafnum( Vector const &point );
+float TraceLeafBrushes( int leafIndex, const Vector &start, const Vector &end, CBaseTrace &traceOut );
+
+//=============================================================================
+
+// dispinfo.cpp
+
+struct SSE_sampleLightOutput_t
+{
+ fltx4 m_flDot[NUM_BUMP_VECTS+1];
+ fltx4 m_flFalloff;
+ fltx4 m_flSunAmount;
+};
+
+#define GATHERLFLAGS_FORCE_FAST 1
+#define GATHERLFLAGS_IGNORE_NORMALS 2
+
+// SSE Gather light stuff
+void GatherSampleLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+ FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+ int nLFlags = 0, // GATHERLFLAGS_xxx
+ int static_prop_to_skip=-1,
+ float flEpsilon = 0.0 );
+//void GatherSampleSkyLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+// int nLFlags = 0,
+// int static_prop_to_skip=-1,
+// float flEpsilon = 0.0 );
+//void GatherSampleAmbientSkySSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+// int nLFlags = 0, // GATHERLFLAGS_xxx
+// int static_prop_to_skip=-1,
+// float flEpsilon = 0.0 );
+//void GatherSampleStandardLightSSE( SSE_sampleLightOutput_t &out, directlight_t *dl, int facenum,
+// FourVectors const& pos, FourVectors *pNormals, int normalCount, int iThread,
+// int nLFlags = 0, // GATHERLFLAGS_xxx
+// int static_prop_to_skip=-1,
+// float flEpsilon = 0.0 );
+
+//-----------------------------------------------------------------------------
+// VRad Displacements
+//-----------------------------------------------------------------------------
+
+struct facelight_t;
+typedef struct radial_s radial_t;
+struct lightinfo_t;
+
+// NOTE: should probably come up with a bsptreetested_t struct or something,
+// see below (PropTested_t)
+struct DispTested_t
+{
+ int m_Enum;
+ int *m_pTested;
+};
+
+class IVRadDispMgr
+{
+public:
+ // creation/destruction
+ virtual void Init( void ) = 0;
+ virtual void Shutdown( void ) = 0;
+
+ // "CalcPoints"
+ virtual bool BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
+ virtual bool BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
+ virtual bool BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace ) = 0;
+
+ // patching functions
+ virtual void MakePatches( void ) = 0;
+ virtual void SubdividePatch( int iPatch ) = 0;
+
+ // pre "FinalLightFace"
+ virtual void InsertSamplesDataIntoHashTable( void ) = 0;
+ virtual void InsertPatchSampleDataIntoHashTable( void ) = 0;
+
+ // "FinalLightFace"
+ virtual radial_t *BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump ) = 0;
+ virtual bool SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch ) = 0;
+ virtual radial_t *BuildPatchRadial( int ndxFace, bool bBump ) = 0;
+
+ // utility
+ virtual void GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal, bool bInside ) = 0;
+ virtual void GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree ) = 0;
+
+ // bsp tree functions
+ virtual bool ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray ) = 0;
+ virtual bool ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf ) = 0;
+ virtual void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord ) = 0;
+ virtual void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, Vector *pNormal ) = 0;
+ virtual void StartRayTest( DispTested_t &dispTested ) = 0;
+ virtual void AddPolysForRayTrace() = 0;
+
+ // general timing -- should be moved!!
+ virtual void StartTimer( const char *name ) = 0;
+ virtual void EndTimer( void ) = 0;
+};
+
+IVRadDispMgr *StaticDispMgr( void );
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+inline bool ValidDispFace( dface_t *pFace )
+{
+ if( !pFace ) { return false; }
+ if( pFace->dispinfo == -1 ) { return false; }
+ if( pFace->numedges != 4 ) { return false; }
+
+ return true;
+}
+
+#define SAMPLEHASH_VOXEL_SIZE 64.0f
+typedef unsigned int SampleHandle_t; // the upper 16 bits = facelight index (works because max face are 65536)
+ // the lower 16 bits = sample index inside of facelight
+struct sample_t;
+struct SampleData_t
+{
+ unsigned short x, y, z;
+ CUtlVector<SampleHandle_t> m_Samples;
+};
+
+struct PatchSampleData_t
+{
+ unsigned short x, y, z;
+ CUtlVector<int> m_ndxPatches;
+};
+
+UtlHashHandle_t SampleData_AddSample( sample_t *pSample, SampleHandle_t sampleHandle );
+void PatchSampleData_AddSample( CPatch *pPatch, int ndxPatch );
+unsigned short IncrementPatchIterationKey();
+void SampleData_Log( void );
+
+extern CUtlHash<SampleData_t> g_SampleHashTable;
+extern CUtlHash<PatchSampleData_t> g_PatchSampleHashTable;
+
+extern int samplesAdded;
+extern int patchSamplesAdded;
+
+//-----------------------------------------------------------------------------
+// Computes lighting for the detail props
+//-----------------------------------------------------------------------------
+
+void ComputeDetailPropLighting( int iThread );
+void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor,
+ int iThread, bool force_fast = false, bool bIgnoreNormals = false );
+
+//-----------------------------------------------------------------------------
+// VRad static props
+//-----------------------------------------------------------------------------
+class IPhysicsCollision;
+struct PropTested_t
+{
+ int m_Enum;
+ int* m_pTested;
+ IPhysicsCollision *pThreadedCollision;
+};
+
+class IVradStaticPropMgr
+{
+public:
+ // methods of IStaticPropMgr
+ virtual void Init() = 0;
+ virtual void Shutdown() = 0;
+ virtual void ComputeLighting( int iThread ) = 0;
+ virtual void AddPolysForRayTrace() = 0;
+};
+
+//extern PropTested_t s_PropTested[MAX_TOOL_THREADS+1];
+extern DispTested_t s_DispTested[MAX_TOOL_THREADS+1];
+
+IVradStaticPropMgr* StaticPropMgr();
+
+extern float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID );
+
+#endif // VRAD_H
diff --git a/mp/src/utils/vrad/vrad_dispcoll.cpp b/mp/src/utils/vrad/vrad_dispcoll.cpp new file mode 100644 index 00000000..df69a4ac --- /dev/null +++ b/mp/src/utils/vrad/vrad_dispcoll.cpp @@ -0,0 +1,1080 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vrad.h"
+#include "VRAD_DispColl.h"
+#include "DispColl_Common.h"
+#include "radial.h"
+#include "CollisionUtils.h"
+#include "tier0\dbg.h"
+
+#define SAMPLE_BBOX_SLOP 5.0f
+#define TRIEDGE_EPSILON 0.001f
+
+float g_flMaxDispSampleSize = 512.0f;
+
+static FileHandle_t pDispFile = FILESYSTEM_INVALID_HANDLE;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRADDispColl::CVRADDispColl()
+{
+ m_iParent = -1;
+
+ m_flSampleRadius2 = 0.0f;
+ m_flPatchSampleRadius2 = 0.0f;
+
+ m_flSampleWidth = 0.0f;
+ m_flSampleHeight = 0.0f;
+
+ m_aLuxelCoords.Purge();
+ m_aVertNormals.Purge();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRADDispColl::~CVRADDispColl()
+{
+ m_aLuxelCoords.Purge();
+ m_aVertNormals.Purge();
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRADDispColl::Create( CCoreDispInfo *pDisp )
+{
+ // Base class create.
+ if( !CDispCollTree::Create( pDisp ) )
+ return false;
+
+ // Allocate VRad specific memory.
+ m_aLuxelCoords.SetSize( GetSize() );
+ m_aVertNormals.SetSize( GetSize() );
+
+ // VRad specific base surface data.
+ CCoreDispSurface *pSurf = pDisp->GetSurface();
+ m_iParent = pSurf->GetHandle();
+
+ // VRad specific displacement surface data.
+ for ( int iVert = 0; iVert < m_aVerts.Count(); ++iVert )
+ {
+ pDisp->GetNormal( iVert, m_aVertNormals[iVert] );
+ pDisp->GetLuxelCoord( 0, iVert, m_aLuxelCoords[iVert] );
+ }
+
+ // Re-calculate the lightmap size (in uv) so that the luxels give
+ // a better world-space uniform approx. due to the non-linear nature
+ // of the displacement surface in uv-space
+ dface_t *pFace = &g_pFaces[m_iParent];
+ if( pFace )
+ {
+ CalcSampleRadius2AndBox( pFace );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CalcSampleRadius2AndBox( dface_t *pFace )
+{
+ // Get the luxel sample size.
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ Assert ( pTexInfo );
+ if ( !pTexInfo )
+ return;
+
+ // Todo: Width = Height now, should change all the code to look at one value.
+ Vector vecTmp( pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][0],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][1],
+ pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][2] );
+ float flWidth = 1.0f / VectorLength( vecTmp );
+ float flHeight = flWidth;
+
+ // Save off the sample width and height.
+ m_flSampleWidth = flWidth;
+ m_flSampleHeight = flHeight;
+
+ // Calculate the sample radius squared.
+ float flSampleRadius = sqrt( ( ( flWidth * flWidth ) + ( flHeight * flHeight ) ) ) * 2.2f;//RADIALDIST2;
+ if ( flSampleRadius > g_flMaxDispSampleSize )
+ {
+ flSampleRadius = g_flMaxDispSampleSize;
+ }
+ m_flSampleRadius2 = flSampleRadius * flSampleRadius;
+
+ // Calculate the patch radius - the max sample edge length * the number of luxels per edge "chop."
+ float flSampleSize = max( m_flSampleWidth, m_flSampleHeight );
+ float flPatchSampleRadius = flSampleSize * dispchop * 2.2f;
+ if ( flPatchSampleRadius > g_MaxDispPatchRadius )
+ {
+ flPatchSampleRadius = g_MaxDispPatchRadius;
+ Warning( "Patch Sample Radius Clamped!\n" );
+ }
+ m_flPatchSampleRadius2 = flPatchSampleRadius * flPatchSampleRadius;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the min/max of the displacement surface.
+//-----------------------------------------------------------------------------
+void CVRADDispColl::GetSurfaceMinMax( Vector &boxMin, Vector &boxMax )
+{
+ // Initialize the minimum and maximum box
+ boxMin = m_aVerts[0];
+ boxMax = m_aVerts[0];
+
+ for( int i = 1; i < m_aVerts.Count(); i++ )
+ {
+ if( m_aVerts[i].x < boxMin.x ) { boxMin.x = m_aVerts[i].x; }
+ if( m_aVerts[i].y < boxMin.y ) { boxMin.y = m_aVerts[i].y; }
+ if( m_aVerts[i].z < boxMin.z ) { boxMin.z = m_aVerts[i].z; }
+
+ if( m_aVerts[i].x > boxMax.x ) { boxMax.x = m_aVerts[i].x; }
+ if( m_aVerts[i].y > boxMax.y ) { boxMax.y = m_aVerts[i].y; }
+ if( m_aVerts[i].z > boxMax.z ) { boxMax.z = m_aVerts[i].z; }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the minor projection axes based on the given normal.
+//-----------------------------------------------------------------------------
+void CVRADDispColl::GetMinorAxes( Vector const &vecNormal, int &nAxis0, int &nAxis1 )
+{
+ nAxis0 = 0;
+ nAxis1 = 1;
+
+ if( FloatMakePositive( vecNormal.x ) > FloatMakePositive( vecNormal.y ) )
+ {
+ if( FloatMakePositive( vecNormal.x ) > FloatMakePositive( vecNormal.z ) )
+ {
+ nAxis0 = 1;
+ nAxis1 = 2;
+ }
+ }
+ else
+ {
+ if( FloatMakePositive( vecNormal.y ) > FloatMakePositive( vecNormal.z ) )
+ {
+ nAxis0 = 0;
+ nAxis1 = 2;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRADDispColl::BaseFacePlaneToDispUV( Vector const &vecPlanePt, Vector2D &dispUV )
+{
+ PointInQuadToBarycentric( m_vecSurfPoints[0], m_vecSurfPoints[3], m_vecSurfPoints[2], m_vecSurfPoints[1], vecPlanePt, dispUV );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurfPoint( Vector2D const &dispUV, Vector &vecPoint, float flPushEps )
+{
+ // Check to see that the point is on the surface.
+ if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f )
+ return;
+
+ // Get the displacement power.
+ int nWidth = ( ( 1 << m_nPower ) + 1 );
+ int nHeight = nWidth;
+
+ // Scale the U, V coordinates to the displacement grid size.
+ float flU = dispUV.x * static_cast<float>( nWidth - 1.000001f );
+ float flV = dispUV.y * static_cast<float>( nHeight - 1.000001f );
+
+ // Find the base U, V.
+ int nSnapU = static_cast<int>( flU );
+ int nSnapV = static_cast<int>( flV );
+
+ // Use this to get the triangle orientation.
+ bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 );
+
+ // Top Left to Bottom Right
+ if( bOdd )
+ {
+ DispUVToSurf_TriTLToBR( vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight );
+ }
+ // Bottom Left to Top Right
+ else
+ {
+ DispUVToSurf_TriBLToTR( vecPoint, flPushEps, flU, flV, nSnapU, nSnapV, nWidth, nHeight );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurf_TriTLToBR( Vector &vecPoint, float flPushEps,
+ float flU, float flV, int nSnapU, int nSnapV,
+ int nWidth, int nHeight )
+{
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ if( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) )
+ {
+ int nIndices[3];
+ nIndices[0] = nNextV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
+ Vector edgeV = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
+ vecPoint = m_aVerts[nIndices[1]] + edgeU * ( 1.0f - flFracU ) + edgeV * ( 1.0f - flFracV );
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeU, edgeV );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+ else
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[0]];
+ Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[0]];
+ vecPoint = m_aVerts[nIndices[0]] + edgeU * flFracU + edgeV * flFracV;
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeU, edgeV );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurf_TriBLToTR( Vector &vecPoint, float flPushEps,
+ float flU, float flV, int nSnapU, int nSnapV,
+ int nWidth, int nHeight )
+{
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ if( flFracU < flFracV )
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nNextV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[2]] - m_aVerts[nIndices[1]];
+ Vector edgeV = m_aVerts[nIndices[0]] - m_aVerts[nIndices[1]];
+ vecPoint = m_aVerts[nIndices[1]] + edgeU * flFracU + edgeV * ( 1.0f - flFracV );
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeV, edgeU );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+ else
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ Vector edgeU = m_aVerts[nIndices[0]] - m_aVerts[nIndices[2]];
+ Vector edgeV = m_aVerts[nIndices[1]] - m_aVerts[nIndices[2]];
+ vecPoint = m_aVerts[nIndices[2]] + edgeU * ( 1.0f - flFracU ) + edgeV * flFracV;
+
+ if ( flPushEps != 0.0f )
+ {
+ Vector vecNormal;
+ vecNormal = CrossProduct( edgeV, edgeU );
+ VectorNormalize( vecNormal );
+ vecPoint += ( vecNormal * flPushEps );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRADDispColl::DispUVToSurfNormal( Vector2D const &dispUV, Vector &vecNormal )
+{
+ // Check to see that the point is on the surface.
+ if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f )
+ return;
+
+ // Get the displacement power.
+ int nWidth = ( ( 1 << m_nPower ) + 1 );
+ int nHeight = nWidth;
+
+ // Scale the U, V coordinates to the displacement grid size.
+ float flU = dispUV.x * static_cast<float>( nWidth - 1.000001f );
+ float flV = dispUV.y * static_cast<float>( nHeight - 1.000001f );
+
+ // Find the base U, V.
+ int nSnapU = static_cast<int>( flU );
+ int nSnapV = static_cast<int>( flV );
+
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ // Get the four normals "around" the "spot"
+ int iQuad[VRAD_QUAD_SIZE];
+ iQuad[0] = ( nSnapV * nWidth ) + nSnapU;
+ iQuad[1] = ( nNextV * nWidth ) + nSnapU;
+ iQuad[2] = ( nNextV * nWidth ) + nNextU;
+ iQuad[3] = ( nSnapV * nWidth ) + nNextU;
+
+ // Find the blended normal (bi-linear).
+ Vector vecTmpNormals[2], vecBlendedNormals[2], vecDispNormals[4];
+
+ for ( int iVert = 0; iVert < VRAD_QUAD_SIZE; ++iVert )
+ {
+ GetVertNormal( iQuad[iVert], vecDispNormals[iVert] );
+ }
+
+ vecTmpNormals[0] = vecDispNormals[0] * ( 1.0f - flFracU );
+ vecTmpNormals[1] = vecDispNormals[3] * flFracU;
+ vecBlendedNormals[0] = vecTmpNormals[0] + vecTmpNormals[1];
+ VectorNormalize( vecBlendedNormals[0] );
+
+ vecTmpNormals[0] = vecDispNormals[1] * ( 1.0f - flFracU );
+ vecTmpNormals[1] = vecDispNormals[2] * flFracU;
+ vecBlendedNormals[1] = vecTmpNormals[0] + vecTmpNormals[1];
+ VectorNormalize( vecBlendedNormals[1] );
+
+ vecTmpNormals[0] = vecBlendedNormals[0] * ( 1.0f - flFracV );
+ vecTmpNormals[1] = vecBlendedNormals[1] * flFracV;
+
+ vecNormal = vecTmpNormals[0] + vecTmpNormals[1];
+ VectorNormalize( vecNormal );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CVRADDispColl::CreateParentPatches( void )
+{
+ // Save the total surface area of the displacement.
+ float flTotalArea = 0.0f;
+
+ // Get the number of displacement subdivisions.
+ int nInterval = GetWidth();
+
+ Vector vecPoints[4];
+ vecPoints[0].Init( m_aVerts[0].x, m_aVerts[0].y, m_aVerts[0].z );
+ vecPoints[1].Init( m_aVerts[(nInterval*(nInterval-1))].x, m_aVerts[(nInterval*(nInterval-1))].y, m_aVerts[(nInterval*(nInterval-1))].z );
+ vecPoints[2].Init( m_aVerts[((nInterval*nInterval)-1)].x, m_aVerts[((nInterval*nInterval)-1)].y, m_aVerts[((nInterval*nInterval)-1)].z );
+ vecPoints[3].Init( m_aVerts[(nInterval-1)].x, m_aVerts[(nInterval-1)].y, m_aVerts[(nInterval-1)].z );
+
+ // Create and initialize the patch.
+ int iPatch = g_Patches.AddToTail();
+ if ( iPatch == g_Patches.InvalidIndex() )
+ return flTotalArea;
+
+ // Keep track of the area of the patches.
+ float flArea = 0.0f;
+ if ( !InitParentPatch( iPatch, vecPoints, flArea ) )
+ {
+ g_Patches.Remove( iPatch );
+ flArea = 0.0f;
+ }
+
+ // Return the displacement area.
+ return flArea;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iParentPatch -
+// nLevel -
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CreateChildPatchesFromRoot( int iParentPatch, int *pChildPatch )
+{
+ // Initialize the child patch indices.
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ pChildPatch[1] = g_Patches.InvalidIndex();
+
+ // Get the number of displacement subdivisions.
+ int nInterval = GetWidth();
+
+ // Get the parent patch.
+ CPatch *pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return;
+
+ // Split along the longest edge.
+ Vector vecEdges[4];
+ vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
+ vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
+ vecEdges[2] = pParentPatch->winding->p[3] - pParentPatch->winding->p[2];
+ vecEdges[3] = pParentPatch->winding->p[3] - pParentPatch->winding->p[0];
+
+ // Should the patch be subdivided - check the area.
+ float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
+ float flMinEdgeLength = flMaxLength * dispchop;
+
+ // Find the longest edge.
+ float flEdgeLength = 0.0f;
+ int iLongEdge = -1;
+ for ( int iEdge = 0; iEdge < 4; ++iEdge )
+ {
+ float flLength = vecEdges[iEdge].Length();
+ if ( flEdgeLength < flLength )
+ {
+ flEdgeLength = vecEdges[iEdge].Length();
+ iLongEdge = iEdge;
+ }
+ }
+
+ // Small enough already, return.
+ if ( flEdgeLength < flMinEdgeLength )
+ return;
+
+ // Test area as well so we don't allow slivers.
+ float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength );
+ Vector vecNormal = vecEdges[3].Cross( vecEdges[0] );
+ float flTestArea = VectorNormalize( vecNormal );
+ if ( flTestArea < flMinArea )
+ return;
+
+ // Get the points for the first triangle.
+ int iPoints[3];
+ Vector vecPoints[3];
+ float flArea;
+
+ iPoints[0] = ( nInterval * nInterval ) - 1;
+ iPoints[1] = 0;
+ iPoints[2] = nInterval * ( nInterval - 1 );
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( m_aVerts[iPoints[iPoint]], vecPoints[iPoint] );
+ }
+
+ // Create and initialize the patch.
+ pChildPatch[0] = g_Patches.AddToTail();
+ if ( pChildPatch[0] == g_Patches.InvalidIndex() )
+ return;
+
+ if ( !InitPatch( pChildPatch[0], iParentPatch, 0, vecPoints, iPoints, flArea ) )
+ {
+ g_Patches.Remove( pChildPatch[0] );
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ return;
+ }
+
+ // Get the points for the second triangle.
+ iPoints[0] = 0;
+ iPoints[1] = ( nInterval * nInterval ) - 1;
+ iPoints[2] = nInterval - 1;
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( m_aVerts[iPoints[iPoint]], vecPoints[iPoint] );
+ }
+
+ // Create and initialize the patch.
+ pChildPatch[1] = g_Patches.AddToTail();
+ if ( pChildPatch[1] == g_Patches.InvalidIndex() )
+ {
+ g_Patches.Remove( pChildPatch[0] );
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ return;
+ }
+
+ if ( !InitPatch( pChildPatch[1], iParentPatch, 1, vecPoints, iPoints, flArea ) )
+ {
+ g_Patches.Remove( pChildPatch[0] );
+ pChildPatch[0] = g_Patches.InvalidIndex();
+ g_Patches.Remove( pChildPatch[1] );
+ pChildPatch[1] = g_Patches.InvalidIndex();
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMinArea -
+// Output : float
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CreateChildPatches( int iParentPatch, int nLevel )
+{
+ // Get the parent patch.
+ CPatch *pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return;
+
+ // The root face is a quad - special case.
+ if ( pParentPatch->winding->numpoints == 4 )
+ {
+ int iChildPatch[2];
+ CreateChildPatchesFromRoot( iParentPatch, iChildPatch );
+ if ( iChildPatch[0] != g_Patches.InvalidIndex() && iChildPatch[1] != g_Patches.InvalidIndex() )
+ {
+ CreateChildPatches( iChildPatch[0], 0 );
+ CreateChildPatches( iChildPatch[1], 0 );
+ }
+ return;
+ }
+
+ // Calculate the the area of the patch (triangle!).
+ Assert( pParentPatch->winding->numpoints == 3 );
+ if ( pParentPatch->winding->numpoints != 3 )
+ return;
+
+ // Should the patch be subdivided - check the area.
+ float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
+ float flMinEdgeLength = flMaxLength * dispchop;
+
+ // Split along the longest edge.
+ Vector vecEdges[3];
+ vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
+ vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[0];
+ vecEdges[2] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
+
+ // Find the longest edge.
+ float flEdgeLength = 0.0f;
+ int iLongEdge = -1;
+ for ( int iEdge = 0; iEdge < 3; ++iEdge )
+ {
+ if ( flEdgeLength < vecEdges[iEdge].Length() )
+ {
+ flEdgeLength = vecEdges[iEdge].Length();
+ iLongEdge = iEdge;
+ }
+ }
+
+ // Small enough already, return.
+ if ( flEdgeLength < flMinEdgeLength )
+ return;
+
+ // Test area as well so we don't allow slivers.
+ float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength ) * 0.5f;
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ float flTestArea = VectorNormalize( vecNormal );
+ flTestArea *= 0.5f;
+ if ( flTestArea < flMinArea )
+ return;
+
+ // Check to see if any more displacement verts exist - go to subdivision if not.
+ if ( nLevel >= ( m_nPower * 2 ) )
+ {
+ CreateChildPatchesSub( iParentPatch );
+ return;
+ }
+
+ int nChildIndices[2][3];
+ int nNewIndex = ( pParentPatch->indices[1] + pParentPatch->indices[0] ) / 2;
+ nChildIndices[0][0] = pParentPatch->indices[2];
+ nChildIndices[0][1] = pParentPatch->indices[0];
+ nChildIndices[0][2] = nNewIndex;
+
+ nChildIndices[1][0] = pParentPatch->indices[1];
+ nChildIndices[1][1] = pParentPatch->indices[2];
+ nChildIndices[1][2] = nNewIndex;
+
+ Vector vecChildPoints[2][3];
+ for ( int iTri = 0; iTri < 2; ++iTri )
+ {
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( m_aVerts[nChildIndices[iTri][iPoint]], vecChildPoints[iTri][iPoint] );
+ }
+ }
+
+ // Create and initialize the children patches.
+ int iChildPatch[2] = { -1, -1 };
+ for ( int iChild = 0; iChild < 2; ++iChild )
+ {
+ iChildPatch[iChild] = g_Patches.AddToTail();
+
+ float flArea = 0.0f;
+ if ( !InitPatch( iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices[iChild], flArea ) )
+ {
+ if ( iChild == 0 )
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ break;
+ }
+ else
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ pParentPatch->child2 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ g_Patches.Remove( iChildPatch[0] );
+ }
+ }
+ }
+
+ // Continue creating children patches.
+ int nNewLevel = ++nLevel;
+ CreateChildPatches( iChildPatch[0], nNewLevel );
+ CreateChildPatches( iChildPatch[1], nNewLevel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMinArea -
+// Output : float
+//-----------------------------------------------------------------------------
+void CVRADDispColl::CreateChildPatchesSub( int iParentPatch )
+{
+ // Get the parent patch.
+ CPatch *pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return;
+
+ // Calculate the the area of the patch (triangle!).
+ Assert( pParentPatch->winding->numpoints == 3 );
+ if ( pParentPatch->winding->numpoints != 3 )
+ return;
+
+ // Should the patch be subdivided - check the area.
+ float flMaxLength = max( m_flSampleWidth, m_flSampleHeight );
+ float flMinEdgeLength = flMaxLength * dispchop;
+
+ // Split along the longest edge.
+ Vector vecEdges[3];
+ vecEdges[0] = pParentPatch->winding->p[1] - pParentPatch->winding->p[0];
+ vecEdges[1] = pParentPatch->winding->p[2] - pParentPatch->winding->p[0];
+ vecEdges[2] = pParentPatch->winding->p[2] - pParentPatch->winding->p[1];
+
+ // Find the longest edge.
+ float flEdgeLength = 0.0f;
+ int iLongEdge = -1;
+ for ( int iEdge = 0; iEdge < 3; ++iEdge )
+ {
+ if ( flEdgeLength < vecEdges[iEdge].Length() )
+ {
+ flEdgeLength = vecEdges[iEdge].Length();
+ iLongEdge = iEdge;
+ }
+ }
+
+ // Small enough already, return.
+ if ( flEdgeLength < flMinEdgeLength )
+ return;
+
+ // Test area as well so we don't allow slivers.
+ float flMinArea = ( dispchop * flMaxLength ) * ( dispchop * flMaxLength ) * 0.5f;
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ float flTestArea = VectorNormalize( vecNormal );
+ flTestArea *= 0.5f;
+ if ( flTestArea < flMinArea )
+ return;
+
+ // Create children patchs - 2 of them.
+ Vector vecChildPoints[2][3];
+ switch ( iLongEdge )
+ {
+ case 0:
+ {
+ vecChildPoints[0][0] = pParentPatch->winding->p[0];
+ vecChildPoints[0][1] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[1] ) * 0.5f;
+ vecChildPoints[0][2] = pParentPatch->winding->p[2];
+
+ vecChildPoints[1][0] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[1] ) * 0.5f;
+ vecChildPoints[1][1] = pParentPatch->winding->p[1];
+ vecChildPoints[1][2] = pParentPatch->winding->p[2];
+ break;
+ }
+ case 1:
+ {
+ vecChildPoints[0][0] = pParentPatch->winding->p[0];
+ vecChildPoints[0][1] = pParentPatch->winding->p[1];
+ vecChildPoints[0][2] = ( pParentPatch->winding->p[1] + pParentPatch->winding->p[2] ) * 0.5f;
+
+ vecChildPoints[1][0] = ( pParentPatch->winding->p[1] + pParentPatch->winding->p[2] ) * 0.5f;
+ vecChildPoints[1][1] = pParentPatch->winding->p[2];
+ vecChildPoints[1][2] = pParentPatch->winding->p[0];
+ break;
+ }
+ case 2:
+ {
+ vecChildPoints[0][0] = pParentPatch->winding->p[0];
+ vecChildPoints[0][1] = pParentPatch->winding->p[1];
+ vecChildPoints[0][2] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[2] ) * 0.5f;
+
+ vecChildPoints[1][0] = ( pParentPatch->winding->p[0] + pParentPatch->winding->p[2] ) * 0.5f;
+ vecChildPoints[1][1] = pParentPatch->winding->p[1];
+ vecChildPoints[1][2] = pParentPatch->winding->p[2];
+ break;
+ }
+ }
+
+
+ // Create and initialize the children patches.
+ int iChildPatch[2] = { 0, 0 };
+ int nChildIndices[3] = { -1, -1, -1 };
+ for ( int iChild = 0; iChild < 2; ++iChild )
+ {
+ iChildPatch[iChild] = g_Patches.AddToTail();
+
+ float flArea = 0.0f;
+ if ( !InitPatch( iChildPatch[iChild], iParentPatch, iChild, vecChildPoints[iChild], nChildIndices, flArea ) )
+ {
+ if ( iChild == 0 )
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ break;
+ }
+ else
+ {
+ pParentPatch->child1 = g_Patches.InvalidIndex();
+ pParentPatch->child2 = g_Patches.InvalidIndex();
+ g_Patches.Remove( iChildPatch[iChild] );
+ g_Patches.Remove( iChildPatch[0] );
+ }
+ }
+ }
+
+ // Continue creating children patches.
+ CreateChildPatchesSub( iChildPatch[0] );
+ CreateChildPatchesSub( iChildPatch[1] );
+}
+
+int PlaneTypeForNormal (Vector& normal)
+{
+ vec_t ax, ay, az;
+
+ // NOTE: should these have an epsilon around 1.0?
+ if (normal[0] == 1.0 || normal[0] == -1.0)
+ return PLANE_X;
+ if (normal[1] == 1.0 || normal[1] == -1.0)
+ return PLANE_Y;
+ if (normal[2] == 1.0 || normal[2] == -1.0)
+ return PLANE_Z;
+
+ ax = fabs(normal[0]);
+ ay = fabs(normal[1]);
+ az = fabs(normal[2]);
+
+ if (ax >= ay && ax >= az)
+ return PLANE_ANYX;
+ if (ay >= ax && ay >= az)
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iPatch -
+// iParentPatch -
+// iChild -
+// *pPoints -
+// *pIndices -
+// &flArea -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CVRADDispColl::InitParentPatch( int iPatch, Vector *pPoints, float &flArea )
+{
+ // Get the current patch.
+ CPatch *pPatch = &g_Patches[iPatch];
+ if ( !pPatch )
+ return false;
+
+ // Clear the patch data.
+ memset( pPatch, 0, sizeof( CPatch ) );
+
+ // This is a parent.
+ pPatch->ndxNext = g_FacePatches.Element( GetParentIndex() );
+ g_FacePatches[GetParentIndex()] = iPatch;
+ pPatch->faceNumber = GetParentIndex();
+
+ // Initialize parent and children indices.
+ pPatch->child1 = g_Patches.InvalidIndex();
+ pPatch->child2 = g_Patches.InvalidIndex();
+ pPatch->parent = g_Patches.InvalidIndex();
+ pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
+ pPatch->ndxNextParent = g_Patches.InvalidIndex();
+
+ Vector vecEdges[2];
+ vecEdges[0] = pPoints[1] - pPoints[0];
+ vecEdges[1] = pPoints[3] - pPoints[0];
+
+ // Calculate the triangle normal and area.
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ flArea = VectorNormalize( vecNormal );
+
+ // Initialize the patch scale.
+ pPatch->scale[0] = pPatch->scale[1] = 1.0f;
+
+ // Set the patch chop - minchop (that is what the minimum area is based on).
+ pPatch->chop = dispchop;
+
+ // Displacements are not sky!
+ pPatch->sky = false;
+
+ // Copy the winding.
+ Vector vecCenter( 0.0f, 0.0f, 0.0f );
+ pPatch->winding = AllocWinding( 4 );
+ pPatch->winding->numpoints = 4;
+ for ( int iPoint = 0; iPoint < 4; ++iPoint )
+ {
+ VectorCopy( pPoints[iPoint], pPatch->winding->p[iPoint] );
+ VectorAdd( pPoints[iPoint], vecCenter, vecCenter );
+ }
+
+ // Set the origin and normal.
+ VectorScale( vecCenter, ( 1.0f / 4.0f ), vecCenter );
+ VectorCopy( vecCenter, pPatch->origin );
+ VectorCopy( vecNormal, pPatch->normal );
+
+ // Create the plane.
+ pPatch->plane = new dplane_t;
+ if ( !pPatch->plane )
+ return false;
+
+ VectorCopy( vecNormal, pPatch->plane->normal );
+ pPatch->plane->dist = vecNormal.Dot( pPoints[0] );
+ pPatch->plane->type = PlaneTypeForNormal( pPatch->plane->normal );
+ pPatch->planeDist = pPatch->plane->dist;
+
+ // Set the area.
+ pPatch->area = flArea;
+
+ // Calculate the mins/maxs.
+ Vector vecMin( FLT_MAX, FLT_MAX, FLT_MAX );
+ Vector vecMax( FLT_MIN, FLT_MIN, FLT_MIN );
+ for ( int iPoint = 0; iPoint < 4; ++iPoint )
+ {
+ for ( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ vecMin[iAxis] = min( vecMin[iAxis], pPoints[iPoint][iAxis] );
+ vecMax[iAxis] = max( vecMax[iAxis], pPoints[iPoint][iAxis] );
+ }
+ }
+
+ VectorCopy( vecMin, pPatch->mins );
+ VectorCopy( vecMax, pPatch->maxs );
+ VectorCopy( vecMin, pPatch->face_mins );
+ VectorCopy( vecMax, pPatch->face_maxs );
+
+ // Check for bumpmap.
+ dface_t *pFace = dfaces + pPatch->faceNumber;
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
+
+ // Misc...
+ pPatch->m_IterationKey = 0;
+
+ // Calculate the base light, area, and reflectivity.
+ BaseLightForFace( &g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPatch -
+// *pPoints -
+// &vecNormal -
+// flArea -
+//-----------------------------------------------------------------------------
+bool CVRADDispColl::InitPatch( int iPatch, int iParentPatch, int iChild, Vector *pPoints, int *pIndices, float &flArea )
+{
+ // Get the current patch.
+ CPatch *pPatch = &g_Patches[iPatch];
+ if ( !pPatch )
+ return false;
+
+ // Clear the patch data.
+ memset( pPatch, 0, sizeof( CPatch ) );
+
+ // Setup the parent if we are not the parent.
+ CPatch *pParentPatch = NULL;
+ if ( iParentPatch != g_Patches.InvalidIndex() )
+ {
+ // Get the parent patch.
+ pParentPatch = &g_Patches[iParentPatch];
+ if ( !pParentPatch )
+ return false;
+ }
+
+ // Attach the face to the correct lists.
+ if ( !pParentPatch )
+ {
+ // This is a parent.
+ pPatch->ndxNext = g_FacePatches.Element( GetParentIndex() );
+ g_FacePatches[GetParentIndex()] = iPatch;
+ pPatch->faceNumber = GetParentIndex();
+ }
+ else
+ {
+ pPatch->ndxNext = g_Patches.InvalidIndex();
+ pPatch->faceNumber = pParentPatch->faceNumber;
+
+ // Attach to the parent patch.
+ if ( iChild == 0 )
+ {
+ pParentPatch->child1 = iPatch;
+ }
+ else
+ {
+ pParentPatch->child2 = iPatch;
+ }
+ }
+
+ // Initialize parent and children indices.
+ pPatch->child1 = g_Patches.InvalidIndex();
+ pPatch->child2 = g_Patches.InvalidIndex();
+ pPatch->ndxNextClusterChild = g_Patches.InvalidIndex();
+ pPatch->ndxNextParent = g_Patches.InvalidIndex();
+ pPatch->parent = iParentPatch;
+
+ // Get triangle edges.
+ Vector vecEdges[3];
+ vecEdges[0] = pPoints[1] - pPoints[0];
+ vecEdges[1] = pPoints[2] - pPoints[0];
+ vecEdges[2] = pPoints[2] - pPoints[1];
+
+ // Find the longest edge.
+// float flEdgeLength = 0.0f;
+// for ( int iEdge = 0; iEdge < 3; ++iEdge )
+// {
+// if ( flEdgeLength < vecEdges[iEdge].Length() )
+// {
+// flEdgeLength = vecEdges[iEdge].Length();
+// }
+// }
+
+ // Calculate the triangle normal and area.
+ Vector vecNormal = vecEdges[1].Cross( vecEdges[0] );
+ flArea = VectorNormalize( vecNormal );
+ flArea *= 0.5f;
+
+ // Initialize the patch scale.
+ pPatch->scale[0] = pPatch->scale[1] = 1.0f;
+
+ // Set the patch chop - minchop (that is what the minimum area is based on).
+ pPatch->chop = dispchop;
+
+ // Displacements are not sky!
+ pPatch->sky = false;
+
+ // Copy the winding.
+ Vector vecCenter( 0.0f, 0.0f, 0.0f );
+ pPatch->winding = AllocWinding( 3 );
+ pPatch->winding->numpoints = 3;
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ VectorCopy( pPoints[iPoint], pPatch->winding->p[iPoint] );
+ VectorAdd( pPoints[iPoint], vecCenter, vecCenter );
+
+ pPatch->indices[iPoint] = static_cast<short>( pIndices[iPoint] );
+ }
+
+ // Set the origin and normal.
+ VectorScale( vecCenter, ( 1.0f / 3.0f ), vecCenter );
+ VectorCopy( vecCenter, pPatch->origin );
+ VectorCopy( vecNormal, pPatch->normal );
+
+ // Create the plane.
+ pPatch->plane = new dplane_t;
+ if ( !pPatch->plane )
+ return false;
+
+ VectorCopy( vecNormal, pPatch->plane->normal );
+ pPatch->plane->dist = vecNormal.Dot( pPoints[0] );
+ pPatch->plane->type = PlaneTypeForNormal( pPatch->plane->normal );
+ pPatch->planeDist = pPatch->plane->dist;
+
+ // Set the area.
+ pPatch->area = flArea;
+
+ // Calculate the mins/maxs.
+ Vector vecMin( FLT_MAX, FLT_MAX, FLT_MAX );
+ Vector vecMax( FLT_MIN, FLT_MIN, FLT_MIN );
+ for ( int iPoint = 0; iPoint < 3; ++iPoint )
+ {
+ for ( int iAxis = 0; iAxis < 3; ++iAxis )
+ {
+ vecMin[iAxis] = min( vecMin[iAxis], pPoints[iPoint][iAxis] );
+ vecMax[iAxis] = max( vecMax[iAxis], pPoints[iPoint][iAxis] );
+ }
+ }
+
+ VectorCopy( vecMin, pPatch->mins );
+ VectorCopy( vecMax, pPatch->maxs );
+
+ if ( !pParentPatch )
+ {
+ VectorCopy( vecMin, pPatch->face_mins );
+ VectorCopy( vecMax, pPatch->face_maxs );
+ }
+ else
+ {
+ VectorCopy( pParentPatch->face_mins, pPatch->face_mins );
+ VectorCopy( pParentPatch->face_maxs, pPatch->face_maxs );
+ }
+
+ // Check for bumpmap.
+ dface_t *pFace = dfaces + pPatch->faceNumber;
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ pPatch->needsBumpmap = pTexInfo->flags & SURF_BUMPLIGHT ? true : false;
+
+ // Misc...
+ pPatch->m_IterationKey = 0;
+
+ // Get the base light for the face.
+ if ( !pParentPatch )
+ {
+ BaseLightForFace( &g_pFaces[pPatch->faceNumber], pPatch->baselight, &pPatch->basearea, pPatch->reflectivity );
+ }
+ else
+ {
+ VectorCopy( pParentPatch->baselight, pPatch->baselight );
+ pPatch->basearea = pParentPatch->basearea;
+ pPatch->reflectivity = pParentPatch->reflectivity;
+ }
+
+ return true;
+}
+
+void CVRADDispColl::AddPolysForRayTrace( void )
+{
+ if ( !( m_nContents & MASK_OPAQUE ) )
+ return;
+
+ for ( int ndxTri = 0; ndxTri < m_aTris.Size(); ndxTri++ )
+ {
+ CDispCollTri *tri = m_aTris.Base() + ndxTri;
+ int v[3];
+ for ( int ndxv = 0; ndxv < 3; ndxv++ )
+ v[ndxv] = tri->GetVert(ndxv);
+
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+ g_RtEnv.AddTriangle( TRACE_ID_OPAQUE, m_aVerts[v[0]], m_aVerts[v[1]], m_aVerts[v[2]], fullCoverage );
+ }
+}
\ No newline at end of file diff --git a/mp/src/utils/vrad/vrad_dispcoll.h b/mp/src/utils/vrad/vrad_dispcoll.h new file mode 100644 index 00000000..668d3118 --- /dev/null +++ b/mp/src/utils/vrad/vrad_dispcoll.h @@ -0,0 +1,80 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VRAD_DISPCOLL_H
+#define VRAD_DISPCOLL_H
+#pragma once
+
+#include <assert.h>
+#include "DispColl_Common.h"
+
+//=============================================================================
+//
+// VRAD specific collision
+//
+#define VRAD_QUAD_SIZE 4
+
+struct CPatch;
+
+class CVRADDispColl : public CDispCollTree
+{
+public:
+
+ // Creation/Destruction Functions
+ CVRADDispColl();
+ ~CVRADDispColl();
+ bool Create( CCoreDispInfo *pDisp );
+
+ // Patches.
+ bool InitPatch( int iPatch, int iParentPatch, int iChild, Vector *pPoints, int *pIndices, float &flArea );
+ bool InitParentPatch( int iPatch, Vector *pPoints, float &flArea );
+ float CreateParentPatches( void );
+ void CreateChildPatches( int iParentPatch, int nLevel );
+ void CreateChildPatchesFromRoot( int iParentPatch, int *pChildPatch );
+ void CreateChildPatchesSub( int iParentPatch );
+
+ // Operations Functions
+ void BaseFacePlaneToDispUV( Vector const &vecPlanePt, Vector2D &dispUV );
+ void DispUVToSurfPoint( Vector2D const &dispUV, Vector &vecPoint, float flPushEps );
+ void DispUVToSurfNormal( Vector2D const &dispUV, Vector &vecNormal );
+
+ // Data.
+ inline float GetSampleRadius2( void ) { return m_flSampleRadius2; }
+ inline float GetPatchSampleRadius2( void ) { return m_flPatchSampleRadius2; }
+
+ inline int GetParentIndex( void ) { return m_iParent; }
+ inline void GetParentFaceNormal( Vector &vecNormal ) { vecNormal = m_vecStabDir; }
+
+ inline void GetVert( int iVert, Vector &vecVert ) { Assert( ( iVert >= 0 ) && ( iVert < GetSize() ) ); vecVert = m_aVerts[iVert]; }
+ inline void GetVertNormal( int iVert, Vector &vecNormal ) { Assert( ( iVert >= 0 ) && ( iVert < GetSize() ) ); vecNormal = m_aVertNormals[iVert]; }
+ inline Vector2D const& GetLuxelCoord( int iLuxel ) { Assert( ( iLuxel >= 0 ) && ( iLuxel < GetSize() ) ); return m_aLuxelCoords[iLuxel]; }
+
+ // Raytracing
+ void AddPolysForRayTrace( void );
+
+protected:
+
+ void CalcSampleRadius2AndBox( dface_t *pFace );
+
+ // Utility.
+ void DispUVToSurf_TriTLToBR( Vector &vecPoint, float flPushEps, float flU, float flV, int nSnapU, int nSnapV, int nWidth, int nHeight );
+ void DispUVToSurf_TriBLToTR( Vector &vecPoint, float flPushEps, float flU, float flV, int nSnapU, int nSnapV, int nWidth, int nHeight );
+ void GetSurfaceMinMax( Vector &boxMin, Vector &boxMax );
+ void GetMinorAxes( Vector const &vecNormal, int &nAxis0, int &nAxis1 );
+
+protected:
+
+ int m_iParent; // Parent index
+ float m_flSampleRadius2; // Sampling radius
+ float m_flPatchSampleRadius2; // Patch sampling radius (max bound)
+ float m_flSampleWidth;
+ float m_flSampleHeight;
+ CUtlVector<Vector2D> m_aLuxelCoords; // Lightmap coordinates.
+ CUtlVector<Vector> m_aVertNormals; // Displacement vertex normals
+};
+
+#endif // VRAD_DISPCOLL_H
\ No newline at end of file diff --git a/mp/src/utils/vrad/vrad_dll-2010.vcxproj b/mp/src/utils/vrad/vrad_dll-2010.vcxproj new file mode 100644 index 00000000..4b41d1a1 --- /dev/null +++ b/mp/src/utils/vrad/vrad_dll-2010.vcxproj @@ -0,0 +1,405 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vrad_dll</ProjectName>
+ <ProjectGuid>{90A78BD4-2532-39D9-6D34-7A3C2648508C}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vrad_dll</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vrad_dll</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi;..\vmpi\mysql\mysqlpp\include;..\vmpi\mysql\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=vrad_dll;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MPI;PROTECTED_THINGS_DISABLE;VRAD;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vrad;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);ws2_32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vrad_dll.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vrad_dll.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi;..\vmpi\mysql\mysqlpp\include;..\vmpi\mysql\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=vrad_dll;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MPI;PROTECTED_THINGS_DISABLE;VRAD;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vrad;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);ws2_32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vrad_dll.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vrad_dll.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib" />
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\raytrace.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vmpi.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ <Library Include="..\..\lib\public\vtf.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="disp_vrad.h" />
+ <ClInclude Include="iincremental.h" />
+ <ClInclude Include="imagepacker.h" />
+ <ClInclude Include="incremental.h" />
+ <ClInclude Include="leaf_ambient_lighting.h" />
+ <ClInclude Include="lightmap.h" />
+ <ClInclude Include="macro_texture.h" />
+ <ClInclude Include="..\..\public\map_utils.h" />
+ <ClInclude Include="mpivrad.h" />
+ <ClInclude Include="radial.h" />
+ <ClInclude Include="..\..\public\bitmap\tgawriter.h" />
+ <ClInclude Include="vismat.h" />
+ <ClInclude Include="vrad.h" />
+ <ClInclude Include="VRAD_DispColl.h" />
+ <ClInclude Include="vraddetailprops.h" />
+ <ClInclude Include="vraddll.h" />
+ <ClInclude Include="..\common\bsplib.h" />
+ <ClInclude Include="..\common\cmdlib.h" />
+ <ClInclude Include="..\common\consolewnd.h" />
+ <ClInclude Include="..\vmpi\ichannel.h" />
+ <ClInclude Include="..\vmpi\imysqlwrapper.h" />
+ <ClInclude Include="..\vmpi\iphelpers.h" />
+ <ClInclude Include="..\common\ISQLDBReplyTarget.h" />
+ <ClInclude Include="..\common\map_shared.h" />
+ <ClInclude Include="..\vmpi\messbuf.h" />
+ <ClInclude Include="..\common\mpi_stats.h" />
+ <ClInclude Include="..\vmpi\mysql_wrapper.h" />
+ <ClInclude Include="..\common\MySqlDatabase.h" />
+ <ClInclude Include="..\common\pacifier.h" />
+ <ClInclude Include="..\common\polylib.h" />
+ <ClInclude Include="..\common\scriplib.h" />
+ <ClInclude Include="..\vmpi\threadhelpers.h" />
+ <ClInclude Include="..\common\threads.h" />
+ <ClInclude Include="..\common\utilmatlib.h" />
+ <ClInclude Include="..\vmpi\vmpi_defs.h" />
+ <ClInclude Include="..\vmpi\vmpi_dispatch.h" />
+ <ClInclude Include="..\vmpi\vmpi_distribute_work.h" />
+ <ClInclude Include="..\vmpi\vmpi_filesystem.h" />
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h" />
+ <ClInclude Include="..\..\public\mathlib\ANORMS.H" />
+ <ClInclude Include="..\..\public\basehandle.h" />
+ <ClInclude Include="..\..\public\tier0\basetypes.h" />
+ <ClInclude Include="..\..\public\tier1\bitbuf.h" />
+ <ClInclude Include="..\..\public\bitvec.h" />
+ <ClInclude Include="..\..\public\BSPFILE.H" />
+ <ClInclude Include="..\..\public\bspflags.h" />
+ <ClInclude Include="..\..\public\BSPTreeData.h" />
+ <ClInclude Include="..\..\public\builddisp.h" />
+ <ClInclude Include="..\..\public\mathlib\bumpvects.h" />
+ <ClInclude Include="..\..\public\tier1\byteswap.h" />
+ <ClInclude Include="..\..\public\tier1\characterset.h" />
+ <ClInclude Include="..\..\public\tier1\checksum_crc.h" />
+ <ClInclude Include="..\..\public\tier1\checksum_md5.h" />
+ <ClInclude Include="..\..\public\ChunkFile.h" />
+ <ClInclude Include="..\..\public\cmodel.h" />
+ <ClInclude Include="..\..\public\CollisionUtils.h" />
+ <ClInclude Include="..\..\public\tier0\commonmacros.h" />
+ <ClInclude Include="..\..\public\mathlib\compressed_vector.h" />
+ <ClInclude Include="..\..\public\const.h" />
+ <ClInclude Include="..\..\public\coordsize.h" />
+ <ClInclude Include="..\..\public\tier0\dbg.h" />
+ <ClInclude Include="..\..\public\disp_common.h" />
+ <ClInclude Include="..\..\public\disp_powerinfo.h" />
+ <ClInclude Include="..\..\public\disp_vertindex.h" />
+ <ClInclude Include="..\..\public\DispColl_Common.h" />
+ <ClInclude Include="..\..\public\tier0\fasttimer.h" />
+ <ClInclude Include="..\..\public\filesystem.h" />
+ <ClInclude Include="..\..\public\filesystem_helpers.h" />
+ <ClInclude Include="..\..\public\GameBSPFile.h" />
+ <ClInclude Include="..\..\public\gametrace.h" />
+ <ClInclude Include="..\..\public\mathlib\halton.h" />
+ <ClInclude Include="..\..\public\materialsystem\hardwareverts.h" />
+ <ClInclude Include="..\..\public\appframework\IAppSystem.h" />
+ <ClInclude Include="..\..\public\tier0\icommandline.h" />
+ <ClInclude Include="..\..\public\ihandleentity.h" />
+ <ClInclude Include="..\..\public\materialsystem\imaterial.h" />
+ <ClInclude Include="..\..\public\materialsystem\imaterialsystem.h" />
+ <ClInclude Include="..\..\public\materialsystem\imaterialvar.h" />
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="..\..\public\iscratchpad3d.h" />
+ <ClInclude Include="..\..\public\ivraddll.h" />
+ <ClInclude Include="..\..\public\materialsystem\materialsystem_config.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ <ClInclude Include="..\..\public\tier0\memdbgon.h" />
+ <ClInclude Include="..\..\public\optimize.h" />
+ <ClInclude Include="..\..\public\phyfile.h" />
+ <ClInclude Include="..\common\physdll.h" />
+ <ClInclude Include="..\..\public\tier0\platform.h" />
+ <ClInclude Include="..\..\public\tier0\protected_things.h" />
+ <ClInclude Include="..\..\public\vstdlib\random.h" />
+ <ClInclude Include="..\..\public\ScratchPad3D.h" />
+ <ClInclude Include="..\..\public\ScratchPadUtils.h" />
+ <ClInclude Include="..\..\public\string_t.h" />
+ <ClInclude Include="..\..\public\tier1\strtools.h" />
+ <ClInclude Include="..\..\public\studio.h" />
+ <ClInclude Include="..\..\public\tier1\tokenreader.h" />
+ <ClInclude Include="..\..\public\trace.h" />
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h" />
+ <ClInclude Include="..\..\public\tier1\utldict.h" />
+ <ClInclude Include="..\..\public\tier1\utlhash.h" />
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h" />
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h" />
+ <ClInclude Include="..\..\public\tier1\utlvector.h" />
+ <ClInclude Include="..\..\public\vcollide.h" />
+ <ClInclude Include="..\..\public\mathlib\vector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector2d.h" />
+ <ClInclude Include="..\..\public\mathlib\vector4d.h" />
+ <ClInclude Include="..\..\public\mathlib\vmatrix.h" />
+ <ClInclude Include="..\vmpi\vmpi.h" />
+ <ClInclude Include="..\..\public\vphysics_interface.h" />
+ <ClInclude Include="..\..\public\mathlib\vplane.h" />
+ <ClInclude Include="..\..\public\tier0\vprof.h" />
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h" />
+ <ClInclude Include="..\..\public\vtf\vtf.h" />
+ <ClInclude Include="..\..\public\wadtypes.h" />
+ <ClInclude Include="..\..\public\worldsize.h" />
+ <ClInclude Include="..\common\vmpi_tools_shared.h" />
+ <ClInclude Include="..\common\tools_minidump.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\BSPTreeData.cpp" />
+ <ClCompile Include="..\..\public\disp_common.cpp" />
+ <ClCompile Include="..\..\public\disp_powerinfo.cpp" />
+ <ClCompile Include="disp_vrad.cpp" />
+ <ClCompile Include="imagepacker.cpp" />
+ <ClCompile Include="incremental.cpp" />
+ <ClCompile Include="leaf_ambient_lighting.cpp" />
+ <ClCompile Include="lightmap.cpp" />
+ <ClCompile Include="..\..\public\loadcmdline.cpp" />
+ <ClCompile Include="..\..\public\lumpfiles.cpp" />
+ <ClCompile Include="macro_texture.cpp" />
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\common\mpi_stats.cpp" />
+ <ClCompile Include="mpivrad.cpp" />
+ <ClCompile Include="..\common\MySqlDatabase.cpp" />
+ <ClCompile Include="..\common\pacifier.cpp" />
+ <ClCompile Include="..\common\physdll.cpp" />
+ <ClCompile Include="radial.cpp" />
+ <ClCompile Include="SampleHash.cpp" />
+ <ClCompile Include="trace.cpp" />
+ <ClCompile Include="..\common\utilmatlib.cpp" />
+ <ClCompile Include="vismat.cpp" />
+ <ClCompile Include="..\common\vmpi_tools_shared.cpp" />
+ <ClCompile Include="vrad.cpp" />
+ <ClCompile Include="VRAD_DispColl.cpp" />
+ <ClCompile Include="VradDetailProps.cpp" />
+ <ClCompile Include="VRadDisps.cpp" />
+ <ClCompile Include="vraddll.cpp" />
+ <ClCompile Include="VRadStaticProps.cpp" />
+ <ClCompile Include="..\..\public\zip_utils.cpp" />
+ <ClCompile Include="..\common\bsplib.cpp" />
+ <ClCompile Include="..\..\public\builddisp.cpp" />
+ <ClCompile Include="..\..\public\ChunkFile.cpp" />
+ <ClCompile Include="..\common\cmdlib.cpp" />
+ <ClCompile Include="..\..\public\DispColl_Common.cpp" />
+ <ClCompile Include="..\common\map_shared.cpp" />
+ <ClCompile Include="..\common\polylib.cpp" />
+ <ClCompile Include="..\common\scriplib.cpp" />
+ <ClCompile Include="..\common\threads.cpp" />
+ <ClCompile Include="..\common\tools_minidump.cpp" />
+ <ClCompile Include="..\..\public\CollisionUtils.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="..\..\public\ScratchPad3D.cpp" />
+ <ClCompile Include="..\..\public\ScratchPadUtils.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="notes.txt" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vrad/vrad_dll-2010.vcxproj.filters b/mp/src/utils/vrad/vrad_dll-2010.vcxproj.filters new file mode 100644 index 00000000..3fbfde77 --- /dev/null +++ b/mp/src/utils/vrad/vrad_dll-2010.vcxproj.filters @@ -0,0 +1,563 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Common Header Files">
+ <UniqueIdentifier>{AFC34ED7-EC78-E112-6213-16C13F1BBFB5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Public Header Files">
+ <UniqueIdentifier>{53AF07E1-D7C4-FEE3-01A5-43636D973BE6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Common Files">
+ <UniqueIdentifier>{A7DC6913-C602-1488-0EDF-DE69D12F2421}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Public Files">
+ <UniqueIdentifier>{A405CE38-5F8E-1A97-6C69-59BB1FF172C4}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\raytrace.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vmpi.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vtf.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="disp_vrad.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="iincremental.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="imagepacker.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="incremental.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="leaf_ambient_lighting.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="lightmap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="macro_texture.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\map_utils.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="mpivrad.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="radial.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\bitmap\tgawriter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vismat.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vrad.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VRAD_DispColl.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vraddetailprops.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vraddll.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\bsplib.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\cmdlib.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\consolewnd.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\ichannel.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\imysqlwrapper.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\iphelpers.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\ISQLDBReplyTarget.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\map_shared.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\messbuf.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\mpi_stats.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\mysql_wrapper.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\MySqlDatabase.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\pacifier.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\polylib.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\scriplib.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\threadhelpers.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\threads.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\utilmatlib.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\vmpi_defs.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\vmpi_dispatch.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\vmpi_distribute_work.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\vmpi_filesystem.h">
+ <Filter>Header Files\Common Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\ANORMS.H">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\basehandle.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\basetypes.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\bitbuf.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\bitvec.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\BSPFILE.H">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\bspflags.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\BSPTreeData.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\builddisp.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\bumpvects.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\byteswap.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\characterset.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\checksum_crc.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\checksum_md5.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ChunkFile.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\cmodel.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\CollisionUtils.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\commonmacros.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\compressed_vector.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\const.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\coordsize.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\dbg.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\disp_common.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\disp_powerinfo.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\disp_vertindex.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\DispColl_Common.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\fasttimer.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\filesystem_helpers.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\GameBSPFile.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\gametrace.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\halton.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\materialsystem\hardwareverts.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\appframework\IAppSystem.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\icommandline.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ihandleentity.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\materialsystem\imaterial.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\materialsystem\imaterialsystem.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\materialsystem\imaterialvar.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\iscratchpad3d.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ivraddll.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\materialsystem\materialsystem_config.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memdbgon.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\optimize.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\phyfile.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\physdll.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\platform.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\protected_things.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\random.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ScratchPad3D.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ScratchPadUtils.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\string_t.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\strtools.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\studio.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\tokenreader.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\trace.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utldict.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlhash.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlvector.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vcollide.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector2d.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector4d.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vmatrix.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\vmpi.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vphysics_interface.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vplane.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\vprof.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vtf\vtf.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\wadtypes.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\worldsize.h">
+ <Filter>Header Files\Public Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\vmpi_tools_shared.h">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\tools_minidump.h">
+ <Filter>Source Files\Common Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\BSPTreeData.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\disp_common.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\disp_powerinfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="disp_vrad.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="imagepacker.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="incremental.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="leaf_ambient_lighting.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="lightmap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\loadcmdline.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\lumpfiles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="macro_texture.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\mpi_stats.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="mpivrad.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\MySqlDatabase.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\pacifier.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\physdll.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="radial.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SampleHash.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="trace.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\utilmatlib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vismat.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\vmpi_tools_shared.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vrad.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VRAD_DispColl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VradDetailProps.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VRadDisps.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vraddll.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VRadStaticProps.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\zip_utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\bsplib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\builddisp.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\ChunkFile.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\cmdlib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\DispColl_Common.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\map_shared.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\polylib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\scriplib.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\threads.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\tools_minidump.cpp">
+ <Filter>Source Files\Common Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\CollisionUtils.cpp">
+ <Filter>Source Files\Public Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Source Files\Public Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\ScratchPad3D.cpp">
+ <Filter>Source Files\Public Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\ScratchPadUtils.cpp">
+ <Filter>Source Files\Public Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="notes.txt">
+ <Filter></Filter>
+ </None>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vrad/vraddetailprops.cpp b/mp/src/utils/vrad/vraddetailprops.cpp new file mode 100644 index 00000000..6712beb1 --- /dev/null +++ b/mp/src/utils/vrad/vraddetailprops.cpp @@ -0,0 +1,1034 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Revision: $
+// $NoKeywords: $
+//
+// This file contains code to allow us to associate client data with bsp leaves.
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "Bsplib.h"
+#include "GameBSPFile.h"
+#include "UtlBuffer.h"
+#include "utlvector.h"
+#include "CModel.h"
+#include "studio.h"
+#include "pacifier.h"
+#include "vraddetailprops.h"
+#include "mathlib/halton.h"
+#include "messbuf.h"
+#include "byteswap.h"
+
+bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Writes a glview text file containing the collision surface in question
+// Input : *pCollide -
+// *pFilename -
+//-----------------------------------------------------------------------------
+void DumpRayToGlView( Ray_t const& ray, float dist, Vector* pColor, const char *pFilename )
+{
+ Vector dir = ray.m_Delta;
+ float len = VectorNormalize(dir);
+ if (len < 1e-3)
+ return;
+
+ Vector up( 0, 0, 1 );
+ Vector crossDir;
+ if (fabs(DotProduct(up, dir)) - 1.0f < -1e-3 )
+ {
+ CrossProduct( dir, up, crossDir );
+ VectorNormalize(crossDir);
+ }
+ else
+ {
+ up.Init( 0, 1, 0 );
+ CrossProduct( dir, up, crossDir );
+ VectorNormalize(crossDir);
+ }
+
+ Vector end;
+ Vector start1, start2;
+ VectorMA( ray.m_Start, dist, ray.m_Delta, end );
+ VectorMA( ray.m_Start, -2, crossDir, start1 );
+ VectorMA( ray.m_Start, 2, crossDir, start2 );
+
+ FileHandle_t fp = g_pFileSystem->Open( pFilename, "a" );
+ int vert = 0;
+ CmdLib_FPrintf( fp, "3\n" );
+ CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start1.x, start1.y, start1.z,
+ pColor->x, pColor->y, pColor->z );
+ vert++;
+ CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", start2.x, start2.y, start2.z,
+ pColor->x, pColor->y, pColor->z );
+ vert++;
+ CmdLib_FPrintf( fp, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", end.x, end.y, end.z,
+ pColor->x, pColor->y, pColor->z );
+ vert++;
+ g_pFileSystem->Close( fp );
+}
+
+
+//-----------------------------------------------------------------------------
+// This puppy is used to construct the game lumps
+//-----------------------------------------------------------------------------
+static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpLDR;
+static CUtlVector<DetailPropLightstylesLump_t> s_DetailPropLightStyleLumpHDR;
+static CUtlVector<DetailPropLightstylesLump_t> *s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
+
+//-----------------------------------------------------------------------------
+// An amount to add to each model to get to the model center
+//-----------------------------------------------------------------------------
+CUtlVector<Vector> g_ModelCenterOffset;
+CUtlVector<Vector> g_SpriteCenterOffset;
+
+void VRadDetailProps_SetHDRMode( bool bHDR )
+{
+ if( bHDR )
+ {
+ s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpHDR;
+ }
+ else
+ {
+ s_pDetailPropLightStyleLump = &s_DetailPropLightStyleLumpLDR;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Finds ambient sky lights
+//-----------------------------------------------------------------------------
+static directlight_t* FindAmbientSkyLight()
+{
+ static directlight_t *s_pCachedSkylight = NULL;
+
+ // Don't keep searching for the same light.
+ if ( !s_pCachedSkylight )
+ {
+ // find any ambient lights
+ directlight_t* dl;
+ for (dl = activelights; dl != 0; dl = dl->next)
+ {
+ if (dl->light.type == emit_skyambient)
+ {
+ s_pCachedSkylight = dl;
+ break;
+ }
+ }
+ }
+
+ return s_pCachedSkylight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute world center of a prop
+//-----------------------------------------------------------------------------
+static void ComputeWorldCenter( DetailObjectLump_t& prop, Vector& center, Vector& normal )
+{
+ // Transform the offset into world space
+ Vector forward, right;
+ AngleVectors( prop.m_Angles, &forward, &right, &normal );
+ VectorCopy( prop.m_Origin, center );
+
+ // FIXME: Take orientation into account?
+ switch (prop.m_Type )
+ {
+ case DETAIL_PROP_TYPE_MODEL:
+ VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].x, forward, center );
+ VectorMA( center, -g_ModelCenterOffset[prop.m_DetailModel].y, right, center );
+ VectorMA( center, g_ModelCenterOffset[prop.m_DetailModel].z, normal, center );
+ break;
+
+ case DETAIL_PROP_TYPE_SPRITE:
+ Vector vecOffset;
+ VectorMultiply( g_SpriteCenterOffset[prop.m_DetailModel], prop.m_flScale, vecOffset );
+ VectorMA( center, vecOffset.x, forward, center );
+ VectorMA( center, -vecOffset.y, right, center );
+ VectorMA( center, vecOffset.z, normal, center );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes max direct lighting for a single detal prop
+//-----------------------------------------------------------------------------
+static void ComputeMaxDirectLighting( DetailObjectLump_t& prop, Vector* maxcolor, int iThread )
+{
+ // The max direct lighting must be along the direction to one
+ // of the static lights....
+
+ Vector origin, normal;
+ ComputeWorldCenter( prop, origin, normal );
+
+ if ( !origin.IsValid() || !normal.IsValid() )
+ {
+ static bool s_Warned = false;
+ if ( !s_Warned )
+ {
+ Warning("WARNING: Bogus detail props encountered!\n" );
+ s_Warned = true;
+ }
+
+ // fill with debug color
+ for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
+ {
+ maxcolor[i].Init(1,0,0);
+ }
+ return;
+ }
+
+ int cluster = ClusterFromPoint(origin);
+
+ Vector delta;
+ CUtlVector< directlight_t* > lights;
+ CUtlVector< Vector > directions;
+
+ directlight_t* dl;
+ for (dl = activelights; dl != 0; dl = dl->next)
+ {
+ // skyambient doesn't affect dlights..
+ if (dl->light.type == emit_skyambient)
+ continue;
+
+ // is this lights cluster visible?
+ if ( PVSCheck( dl->pvs, cluster ) )
+ {
+ lights.AddToTail(dl);
+ VectorSubtract( dl->light.origin, origin, delta );
+ VectorNormalize( delta );
+ directions.AddToTail( delta );
+ }
+ }
+
+ // Find the max illumination
+ int i;
+ for ( i = 0; i < MAX_LIGHTSTYLES; ++i)
+ {
+ maxcolor[i].Init(0,0,0);
+ }
+
+ // NOTE: See version 10 for a method where we choose a normal based on whichever
+ // one produces the maximum possible illumination. This appeared to work better on
+ // e3_town, so I'm trying it now; hopefully it'll be good for all cases.
+ int j;
+ for ( j = 0; j < lights.Count(); ++j)
+ {
+ dl = lights[j];
+
+ SSE_sampleLightOutput_t out;
+ FourVectors origin4;
+ FourVectors normal4;
+ origin4.DuplicateVector( origin );
+ normal4.DuplicateVector( normal );
+
+ GatherSampleLightSSE ( out, dl, -1, origin4, &normal4, 1, iThread );
+ VectorMA( maxcolor[dl->light.style], out.m_flFalloff.m128_f32[0] * out.m_flDot[0].m128_f32[0], dl->light.intensity, maxcolor[dl->light.style] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the ambient term from a particular surface
+//-----------------------------------------------------------------------------
+
+static void ComputeAmbientFromSurface( dface_t* pFace, directlight_t* pSkylight,
+ Vector& radcolor )
+{
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+ if (pTex)
+ {
+ // If we hit the sky, use the sky ambient
+ if (pTex->flags & SURF_SKY)
+ {
+ if (pSkylight)
+ {
+ // add in sky ambient
+ VectorDivide( pSkylight->light.intensity, 255.0f, radcolor );
+ }
+ }
+ else
+ {
+ VectorMultiply( radcolor, dtexdata[pTex->texdata].reflectivity, radcolor );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the lightmap color at a particular point
+//-----------------------------------------------------------------------------
+
+static void ComputeLightmapColorFromAverage( dface_t* pFace, directlight_t* pSkylight, float scale, Vector pColor[MAX_LIGHTSTYLES] )
+{
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+ if (pTex->flags & SURF_SKY)
+ {
+ if (pSkylight)
+ {
+ // add in sky ambient
+ Vector amb = pSkylight->light.intensity / 255.0f;
+ pColor[0] += amb * scale;
+ }
+ return;
+ }
+
+ for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
+ {
+ ColorRGBExp32* pAvgColor = dface_AvgLightColor( pFace, maps );
+
+ // this code expects values from [0..1] not [0..255]
+ Vector color;
+ color[0] = TexLightToLinear( pAvgColor->r, pAvgColor->exponent );
+ color[1] = TexLightToLinear( pAvgColor->g, pAvgColor->exponent );
+ color[2] = TexLightToLinear( pAvgColor->b, pAvgColor->exponent );
+
+ ComputeAmbientFromSurface( pFace, pSkylight, color );
+
+ int style = pFace->styles[maps];
+ pColor[style] += color * scale;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the surface has bumped lightmaps
+//-----------------------------------------------------------------------------
+
+static bool SurfHasBumpedLightmaps( dface_t *pSurf )
+{
+ bool hasBumpmap = false;
+ if( ( texinfo[pSurf->texinfo].flags & SURF_BUMPLIGHT ) &&
+ ( !( texinfo[pSurf->texinfo].flags & SURF_NOLIGHT ) ) )
+ {
+ hasBumpmap = true;
+ }
+ return hasBumpmap;
+}
+
+//-----------------------------------------------------------------------------
+// Computes the lightmap color at a particular point
+//-----------------------------------------------------------------------------
+
+static void ComputeLightmapColorPointSample( dface_t* pFace, directlight_t* pSkylight, Vector2D const& luv, float scale, Vector pColor[MAX_LIGHTSTYLES] )
+{
+ // face unaffected by light
+ if (pFace->lightofs == -1 )
+ return;
+
+ int smax = ( pFace->m_LightmapTextureSizeInLuxels[0] ) + 1;
+ int tmax = ( pFace->m_LightmapTextureSizeInLuxels[1] ) + 1;
+
+ // luv is in the space of the accumulated lightmap page; we need to convert
+ // it to be in the space of the surface
+ int ds = clamp( (int)luv.x, 0, smax-1 );
+ int dt = clamp( (int)luv.y, 0, tmax-1 );
+
+ int offset = smax * tmax;
+ if ( SurfHasBumpedLightmaps( pFace ) )
+ offset *= ( NUM_BUMP_VECTS + 1 );
+
+ ColorRGBExp32* pLightmap = (ColorRGBExp32*)&pdlightdata->Base()[pFace->lightofs];
+ pLightmap += dt * smax + ds;
+ for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps)
+ {
+ int style = pFace->styles[maps];
+
+ Vector color;
+ color[0] = TexLightToLinear( pLightmap->r, pLightmap->exponent );
+ color[1] = TexLightToLinear( pLightmap->g, pLightmap->exponent );
+ color[2] = TexLightToLinear( pLightmap->b, pLightmap->exponent );
+
+ ComputeAmbientFromSurface( pFace, pSkylight, color );
+ pColor[style] += color * scale;
+
+ pLightmap += offset;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests a particular node
+//-----------------------------------------------------------------------------
+
+class CLightSurface : public IBSPNodeEnumerator
+{
+public:
+ CLightSurface(int iThread) : m_pSurface(0), m_HitFrac(1.0f), m_bHasLuxel(false), m_iThread(iThread) {}
+
+ // call back with a node and a context
+ bool EnumerateNode( int node, Ray_t const& ray, float f, int context )
+ {
+ dface_t* pSkySurface = 0;
+
+ // Compute the actual point
+ Vector pt;
+ VectorMA( ray.m_Start, f, ray.m_Delta, pt );
+
+ dnode_t* pNode = &dnodes[node];
+ dface_t* pFace = &g_pFaces[pNode->firstface];
+ for (int i=0 ; i < pNode->numfaces ; ++i, ++pFace)
+ {
+ // Don't take into account faces that are int a leaf
+ if ( !pFace->onNode )
+ continue;
+
+ // Don't test displacement faces
+ if ( pFace->dispinfo != -1 )
+ continue;
+
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+
+ // Don't immediately return when we hit sky;
+ // we may actually hit another surface
+ if (pTex->flags & SURF_SKY)
+ {
+ if (TestPointAgainstSkySurface( pt, pFace ))
+ {
+ pSkySurface = pFace;
+ }
+
+ continue;
+ }
+
+ if (TestPointAgainstSurface( pt, pFace, pTex ))
+ {
+ m_HitFrac = f;
+ m_pSurface = pFace;
+ m_bHasLuxel = true;
+ return false;
+ }
+ }
+
+ // if we hit a sky surface, return it
+ m_pSurface = pSkySurface;
+ return (m_pSurface == 0);
+ }
+
+ // call back with a leaf and a context
+ virtual bool EnumerateLeaf( int leaf, Ray_t const& ray, float start, float end, int context )
+ {
+ bool hit = false;
+ dleaf_t* pLeaf = &dleafs[leaf];
+ for (int i=0 ; i < pLeaf->numleaffaces ; ++i)
+ {
+ Assert( pLeaf->firstleafface + i < numleaffaces );
+ Assert( dleaffaces[pLeaf->firstleafface + i] < numfaces );
+ dface_t* pFace = &g_pFaces[dleaffaces[pLeaf->firstleafface + i]];
+
+ // Don't test displacement faces; we need to check another list
+ if ( pFace->dispinfo != -1 )
+ continue;
+
+ // Don't take into account faces that are on a node
+ if ( pFace->onNode )
+ continue;
+
+ // Find intersection point against detail brushes
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+
+ dplane_t* pPlane = &dplanes[pFace->planenum];
+
+ // Backface cull...
+ if (DotProduct( pPlane->normal, ray.m_Delta ) > 0)
+ continue;
+
+ float startDotN = DotProduct( ray.m_Start, pPlane->normal );
+ float deltaDotN = DotProduct( ray.m_Delta, pPlane->normal );
+
+ float front = startDotN + start * deltaDotN - pPlane->dist;
+ float back = startDotN + end * deltaDotN - pPlane->dist;
+
+ int side = front < 0;
+
+ // Blow it off if it doesn't split the plane...
+ if ( (back < 0) == side )
+ continue;
+
+ // Don't test a surface that is farther away from the closest found intersection
+ float f = front / (front-back);
+ float mid = start * (1.0f - f) + end * f;
+ if (mid >= m_HitFrac)
+ continue;
+
+ Vector pt;
+ VectorMA( ray.m_Start, mid, ray.m_Delta, pt );
+
+ if (TestPointAgainstSurface( pt, pFace, pTex ))
+ {
+ m_HitFrac = mid;
+ m_pSurface = pFace;
+ hit = true;
+ m_bHasLuxel = true;
+ }
+ }
+
+ // Now try to clip against all displacements in the leaf
+ float dist;
+ Vector2D luxelCoord;
+ dface_t *pDispFace;
+ StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[m_iThread], ray, leaf, dist, pDispFace, luxelCoord );
+ if (dist < m_HitFrac)
+ {
+ m_HitFrac = dist;
+ m_pSurface = pDispFace;
+ Vector2DCopy( luxelCoord, m_LuxelCoord );
+ hit = true;
+ m_bHasLuxel = true;
+ }
+ return !hit;
+ }
+
+ bool FindIntersection( Ray_t const& ray )
+ {
+ StaticDispMgr()->StartRayTest( s_DispTested[m_iThread] );
+ return !EnumerateNodesAlongRay( ray, this, 0 );
+ }
+
+private:
+ bool TestPointAgainstSurface( Vector const& pt, dface_t* pFace, texinfo_t* pTex )
+ {
+ // no lightmaps on this surface? punt...
+ // FIXME: should be water surface?
+ if (pTex->flags & SURF_NOLIGHT)
+ return false;
+
+ // See where in lightmap space our intersection point is
+ float s, t;
+ s = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[0]) +
+ pTex->lightmapVecsLuxelsPerWorldUnits[0][3];
+ t = DotProduct (pt.Base(), pTex->lightmapVecsLuxelsPerWorldUnits[1]) +
+ pTex->lightmapVecsLuxelsPerWorldUnits[1][3];
+
+ // Not in the bounds of our lightmap? punt...
+ if( s < pFace->m_LightmapTextureMinsInLuxels[0] || t < pFace->m_LightmapTextureMinsInLuxels[1] )
+ return false;
+
+ // assuming a square lightmap (FIXME: which ain't always the case),
+ // lets see if it lies in that rectangle. If not, punt...
+ float ds = s - pFace->m_LightmapTextureMinsInLuxels[0];
+ float dt = t - pFace->m_LightmapTextureMinsInLuxels[1];
+ if( ds > pFace->m_LightmapTextureSizeInLuxels[0] || dt > pFace->m_LightmapTextureSizeInLuxels[1] )
+ return false;
+
+ m_LuxelCoord.x = ds;
+ m_LuxelCoord.y = dt;
+
+ return true;
+ }
+
+ bool TestPointAgainstSkySurface( Vector const &pt, dface_t *pFace )
+ {
+ // Create sky face winding.
+ winding_t *pWinding = WindingFromFace( pFace, Vector( 0.0f, 0.0f, 0.0f ) );
+
+ // Test point in winding. (Since it is at the node, it is in the plane.)
+ bool bRet = PointInWinding( pt, pWinding );
+
+ FreeWinding( pWinding );
+
+ return bRet;
+ }
+
+
+public:
+ int m_iThread;
+ dface_t* m_pSurface;
+ float m_HitFrac;
+ Vector2D m_LuxelCoord;
+ bool m_bHasLuxel;
+};
+
+bool CastRayInLeaf( int iThread, const Vector &start, const Vector &end, int leafIndex, float *pFraction, Vector *pNormal )
+{
+ pFraction[0] = 1.0f;
+
+ Ray_t ray;
+ ray.Init( start, end, vec3_origin, vec3_origin );
+ CBaseTrace trace;
+ if ( TraceLeafBrushes( leafIndex, start, end, trace ) != 1.0f )
+ {
+ pFraction[0] = trace.fraction;
+ *pNormal = trace.plane.normal;
+ }
+ else
+ {
+ Assert(!trace.startsolid && !trace.allsolid);
+ }
+ StaticDispMgr()->StartRayTest( s_DispTested[iThread] );
+ // Now try to clip against all displacements in the leaf
+ float dist;
+ Vector normal;
+ StaticDispMgr()->ClipRayToDispInLeaf( s_DispTested[iThread], ray, leafIndex, dist, &normal );
+ if ( dist < pFraction[0] )
+ {
+ pFraction[0] = dist;
+ *pNormal = normal;
+ }
+ return pFraction[0] != 1.0f ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Computes ambient lighting along a specified ray.
+// Ray represents a cone, tanTheta is the tan of the inner cone angle
+//-----------------------------------------------------------------------------
+void CalcRayAmbientLighting( int iThread, const Vector &vStart, const Vector &vEnd, float tanTheta, Vector color[MAX_LIGHTSTYLES] )
+{
+ Ray_t ray;
+ ray.Init( vStart, vEnd, vec3_origin, vec3_origin );
+
+ directlight_t *pSkyLight = FindAmbientSkyLight();
+
+ CLightSurface surfEnum(iThread);
+ if (!surfEnum.FindIntersection( ray ))
+ return;
+
+ // compute the approximate radius of a circle centered around the intersection point
+ float dist = ray.m_Delta.Length() * tanTheta * surfEnum.m_HitFrac;
+
+ // until 20" we use the point sample, then blend in the average until we're covering 40"
+ // This is attempting to model the ray as a cone - in the ideal case we'd simply sample all
+ // luxels in the intersection of the cone with the surface. Since we don't have surface
+ // neighbor information computed we'll just approximate that sampling with a blend between
+ // a point sample and the face average.
+ // This yields results that are similar in that aliasing is reduced at distance while
+ // point samples provide accuracy for intersections with near geometry
+ float scaleAvg = RemapValClamped( dist, 20, 40, 0.0f, 1.0f );
+
+ if ( !surfEnum.m_bHasLuxel )
+ {
+ // don't have luxel UV, so just use average sample
+ scaleAvg = 1.0;
+ }
+ float scaleSample = 1.0f - scaleAvg;
+
+ if (scaleAvg != 0)
+ {
+ ComputeLightmapColorFromAverage( surfEnum.m_pSurface, pSkyLight, scaleAvg, color );
+ }
+ if (scaleSample != 0)
+ {
+ ComputeLightmapColorPointSample( surfEnum.m_pSurface, pSkyLight, surfEnum.m_LuxelCoord, scaleSample, color );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compute ambient lighting component at specified position.
+//-----------------------------------------------------------------------------
+static void ComputeAmbientLightingAtPoint( int iThread, const Vector &origin, Vector radcolor[NUMVERTEXNORMALS], Vector color[MAX_LIGHTSTYLES] )
+{
+ // NOTE: I'm not dealing with shadow-casting static props here
+ // This is for speed, although we can add it if it turns out to
+ // be important
+
+ // sample world by casting N rays distributed across a sphere
+ Vector upend;
+
+ int j;
+ for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
+ {
+ color[j].Init( 0,0,0 );
+ }
+
+ float tanTheta = tan(VERTEXNORMAL_CONE_INNER_ANGLE);
+ for (int i = 0; i < NUMVERTEXNORMALS; i++)
+ {
+ VectorMA( origin, COORD_EXTENT * 1.74, g_anorms[i], upend );
+
+ // Now that we've got a ray, see what surface we've hit
+ CalcRayAmbientLighting( iThread, origin, upend, tanTheta, color );
+
+// DumpRayToGlView( ray, surfEnum.m_HitFrac, &color[0], "test.out" );
+ }
+
+ for ( j = 0; j < MAX_LIGHTSTYLES; ++j)
+ {
+ VectorMultiply( color[j], 255.0f / (float)NUMVERTEXNORMALS, color[j] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Trace hemispherical rays from a vertex, accumulating indirect
+// sources at each ray termination.
+//-----------------------------------------------------------------------------
+void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor,
+ int iThread, bool force_fast, bool bIgnoreNormals )
+{
+ Ray_t ray;
+ CLightSurface surfEnum(iThread);
+
+ outColor.Init();
+
+
+ int nSamples = NUMVERTEXNORMALS;
+ if ( do_fast || force_fast )
+ nSamples /= 4;
+ else
+ nSamples *= g_flSkySampleScale;
+
+ float totalDot = 0;
+ DirectionalSampler_t sampler;
+ for (int j = 0; j < nSamples; j++)
+ {
+ Vector samplingNormal = sampler.NextValue();
+ float dot;
+
+ if ( bIgnoreNormals )
+ dot = (0.7071/2);
+ else
+ dot = DotProduct( normal, samplingNormal );
+
+ if ( dot <= EQUAL_EPSILON )
+ {
+ // reject angles behind our plane
+ continue;
+ }
+
+ totalDot += dot;
+
+ // trace to determine surface
+ Vector vEnd;
+ VectorScale( samplingNormal, MAX_TRACE_LENGTH, vEnd );
+ VectorAdd( position, vEnd, vEnd );
+
+ ray.Init( position, vEnd, vec3_origin, vec3_origin );
+ if ( !surfEnum.FindIntersection( ray ) )
+ continue;
+
+ // get color from surface lightmap
+ texinfo_t* pTex = &texinfo[surfEnum.m_pSurface->texinfo];
+ if ( !pTex || pTex->flags & SURF_SKY )
+ {
+ // ignore contribution from sky
+ // sky ambient already accounted for during direct pass
+ continue;
+ }
+
+ if ( surfEnum.m_pSurface->styles[0] == 255 || surfEnum.m_pSurface->lightofs < 0 )
+ {
+ // no light affects this face
+ continue;
+ }
+
+
+ Vector lightmapColor;
+ if ( !surfEnum.m_bHasLuxel )
+ {
+ ColorRGBExp32* pAvgLightmapColor = dface_AvgLightColor( surfEnum.m_pSurface, 0 );
+ ColorRGBExp32ToVector( *pAvgLightmapColor, lightmapColor );
+ }
+ else
+ {
+ // get color from displacement
+ int smax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[0] ) + 1;
+ int tmax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[1] ) + 1;
+
+ // luxelcoord is in the space of the accumulated lightmap page; we need to convert
+ // it to be in the space of the surface
+ int ds = clamp( (int)surfEnum.m_LuxelCoord.x, 0, smax-1 );
+ int dt = clamp( (int)surfEnum.m_LuxelCoord.y, 0, tmax-1 );
+
+ ColorRGBExp32* pLightmap = (ColorRGBExp32*)&(*pdlightdata)[surfEnum.m_pSurface->lightofs];
+ pLightmap += dt * smax + ds;
+ ColorRGBExp32ToVector( *pLightmap, lightmapColor );
+ }
+
+ VectorMultiply( lightmapColor, dtexdata[pTex->texdata].reflectivity, lightmapColor );
+ VectorAdd( outColor, lightmapColor, outColor );
+ }
+
+ if ( totalDot )
+ {
+ VectorScale( outColor, 1.0f/totalDot, outColor );
+ }
+}
+
+static void ComputeAmbientLighting( int iThread, DetailObjectLump_t& prop, Vector color[MAX_LIGHTSTYLES] )
+{
+ Vector origin, normal;
+ ComputeWorldCenter( prop, origin, normal );
+
+ if ( !origin.IsValid() || !normal.IsValid() )
+ {
+ static bool s_Warned = false;
+ if ( !s_Warned )
+ {
+ Warning("WARNING: Bogus detail props encountered!\n" );
+ s_Warned = true;
+ }
+
+ // fill with debug color
+ for ( int i = 0; i < MAX_LIGHTSTYLES; ++i)
+ {
+ color[i].Init(1,0,0);
+ }
+ return;
+ }
+
+ Vector radcolor[NUMVERTEXNORMALS];
+ ComputeAmbientLightingAtPoint( iThread, origin, radcolor, color );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes lighting for a single detal prop
+//-----------------------------------------------------------------------------
+
+static void ComputeLighting( DetailObjectLump_t& prop, int iThread )
+{
+ // We're going to take the maximum of the ambient lighting and
+ // the strongest directional light. This works because we're assuming
+ // the props will have built-in faked lighting.
+
+ Vector directColor[MAX_LIGHTSTYLES];
+ Vector ambColor[MAX_LIGHTSTYLES];
+
+ // Get the max influence of all direct lights
+ ComputeMaxDirectLighting( prop, directColor, iThread );
+
+ // Get the ambient lighting + lightstyles
+ ComputeAmbientLighting( iThread, prop, ambColor );
+
+ // Base lighting
+ Vector totalColor;
+ VectorAdd( directColor[0], ambColor[0], totalColor );
+ VectorToColorRGBExp32( totalColor, prop.m_Lighting );
+
+ bool hasLightstyles = false;
+ prop.m_LightStyleCount = 0;
+
+ // lightstyles
+ for (int i = 1; i < MAX_LIGHTSTYLES; ++i )
+ {
+ VectorAdd( directColor[i], ambColor[i], totalColor );
+ totalColor *= 0.5f;
+
+ if ((totalColor[0] != 0.0f) || (totalColor[1] != 0.0f) ||
+ (totalColor[2] != 0.0f) )
+ {
+ if (!hasLightstyles)
+ {
+ prop.m_LightStyles = s_pDetailPropLightStyleLump->Size();
+ hasLightstyles = true;
+ }
+
+ int j = s_pDetailPropLightStyleLump->AddToTail();
+ VectorToColorRGBExp32( totalColor, (*s_pDetailPropLightStyleLump)[j].m_Lighting );
+ (*s_pDetailPropLightStyleLump)[j].m_Style = i;
+ ++prop.m_LightStyleCount;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialization
+//-----------------------------------------------------------------------------
+static void UnserializeModelDict( CUtlBuffer& buf )
+{
+ // Get origin offset for each model...
+ int count = buf.GetInt();
+ while ( --count >= 0 )
+ {
+ DetailObjectDictLump_t lump;
+ buf.Get( &lump, sizeof(DetailObjectDictLump_t) );
+
+ int i = g_ModelCenterOffset.AddToTail();
+
+ CUtlBuffer mdlbuf;
+ if (LoadStudioModel( lump.m_Name, mdlbuf ))
+ {
+ studiohdr_t* pHdr = (studiohdr_t*)mdlbuf.Base();
+ VectorAdd( pHdr->hull_min, pHdr->hull_max, g_ModelCenterOffset[i] );
+ g_ModelCenterOffset[i] *= 0.5f;
+ }
+ else
+ {
+ g_ModelCenterOffset[i].Init(0,0,0);
+ }
+ }
+}
+
+static void UnserializeSpriteDict( CUtlBuffer& buf )
+{
+ // Get origin offset for each model...
+ int count = buf.GetInt();
+ while ( --count >= 0 )
+ {
+ DetailSpriteDictLump_t lump;
+ buf.Get( &lump, sizeof(DetailSpriteDictLump_t) );
+
+ // For these sprites, x goes out the front, y right, z up
+ int i = g_SpriteCenterOffset.AddToTail();
+ g_SpriteCenterOffset[i].x = 0.0f;
+ g_SpriteCenterOffset[i].y = lump.m_LR.x + lump.m_UL.x;
+ g_SpriteCenterOffset[i].z = lump.m_LR.y + lump.m_UL.y;
+ g_SpriteCenterOffset[i] *= 0.5f;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserializes the detail props
+//-----------------------------------------------------------------------------
+static int UnserializeDetailProps( DetailObjectLump_t*& pProps )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_DETAIL_PROPS );
+
+ if (g_GameLumps.GetGameLumpVersion(handle) != GAMELUMP_DETAIL_PROPS_VERSION)
+ return 0;
+
+ // Unserialize
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
+
+ UnserializeModelDict( buf );
+ UnserializeSpriteDict( buf );
+
+ // Now we're pointing to the detail prop data
+ // This actually works because the scope of the game lump data
+ // is global and the buf was just pointing to it.
+ int count = buf.GetInt();
+ if (count)
+ {
+ pProps = (DetailObjectLump_t*)buf.PeekGet();
+ }
+ else
+ {
+ pProps = 0;
+ }
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes the detail lighting lump
+//-----------------------------------------------------------------------------
+static void WriteDetailLightingLump( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle(lumpID);
+ if (handle != g_GameLumps.InvalidGameLump())
+ g_GameLumps.DestroyGameLump(handle);
+ int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
+ int lumpsize = lightsize + sizeof(int);
+
+ handle = g_GameLumps.CreateGameLump( lumpID, lumpsize, 0, lumpVersion );
+
+ // Serialize the data
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), lumpsize );
+ buf.PutInt( lumpData.Size() );
+ if (lightsize)
+ buf.Put( lumpData.Base(), lightsize );
+}
+
+static void WriteDetailLightingLumps( void )
+{
+ WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
+ WriteDetailLightingLump( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
+}
+
+// need to do this so that if we are building HDR data, the LDR data is intact, and vice versa.s
+void UnserializeDetailPropLighting( int lumpID, int lumpVersion, CUtlVector<DetailPropLightstylesLump_t> &lumpData )
+{
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( lumpID );
+
+ if( handle == g_GameLumps.InvalidGameLump() )
+ {
+ return;
+ }
+
+ if (g_GameLumps.GetGameLumpVersion(handle) != lumpVersion)
+ return;
+
+ // Unserialize
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), g_GameLumps.GameLumpSize( handle ), CUtlBuffer::READ_ONLY );
+
+ int count = buf.GetInt();
+ if( !count )
+ {
+ return;
+ }
+ lumpData.SetCount( count );
+ int lightsize = lumpData.Size() * sizeof(DetailPropLightstylesLump_t);
+ buf.Get( lumpData.Base(), lightsize );
+}
+
+DetailObjectLump_t *g_pMPIDetailProps = NULL;
+
+void VMPI_ProcessDetailPropWU( int iThread, int iWorkUnit, MessageBuffer *pBuf )
+{
+ CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
+
+ DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
+ ComputeLighting( prop, iThread );
+
+ // Send the results back...
+ pBuf->write( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
+ pBuf->write( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
+ pBuf->write( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
+
+ for ( int i=0; i < prop.m_LightStyleCount; i++ )
+ {
+ DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
+ pBuf->write( &l->m_Lighting, sizeof( l->m_Lighting ) );
+ pBuf->write( &l->m_Style, sizeof( l->m_Style ) );
+ }
+}
+
+
+void VMPI_ReceiveDetailPropWU( int iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ CUtlVector<DetailPropLightstylesLump_t> *pDetailPropLump = s_pDetailPropLightStyleLump;
+
+ DetailObjectLump_t& prop = g_pMPIDetailProps[iWorkUnit];
+
+ pBuf->read( &prop.m_Lighting, sizeof( prop.m_Lighting ) );
+ pBuf->read( &prop.m_LightStyleCount, sizeof( prop.m_LightStyleCount ) );
+ pBuf->read( &prop.m_LightStyles, sizeof( prop.m_LightStyles ) );
+
+ pDetailPropLump->EnsureCount( prop.m_LightStyles + prop.m_LightStyleCount );
+
+ for ( int i=0; i < prop.m_LightStyleCount; i++ )
+ {
+ DetailPropLightstylesLump_t *l = &pDetailPropLump->Element( i + prop.m_LightStyles );
+ pBuf->read( &l->m_Lighting, sizeof( l->m_Lighting ) );
+ pBuf->read( &l->m_Style, sizeof( l->m_Style ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Computes lighting for the detail props
+//-----------------------------------------------------------------------------
+void ComputeDetailPropLighting( int iThread )
+{
+ // illuminate them all
+ DetailObjectLump_t* pProps;
+ int count = UnserializeDetailProps( pProps );
+ if (!count)
+ return;
+
+ // unserialize the lump that we aren't computing.
+ if( g_bHDR )
+ {
+ UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING, GAMELUMP_DETAIL_PROP_LIGHTING_VERSION, s_DetailPropLightStyleLumpLDR );
+ }
+ else
+ {
+ UnserializeDetailPropLighting( GAMELUMP_DETAIL_PROP_LIGHTING_HDR, GAMELUMP_DETAIL_PROP_LIGHTING_HDR_VERSION, s_DetailPropLightStyleLumpHDR );
+ }
+
+ StartPacifier("Computing detail prop lighting : ");
+
+ for (int i = 0; i < count; ++i)
+ {
+ UpdatePacifier( (float)i / (float)count );
+ ComputeLighting( pProps[i], iThread );
+ }
+
+ // Write detail prop lightstyle lump...
+ WriteDetailLightingLumps();
+ EndPacifier( true );
+}
diff --git a/mp/src/utils/vrad/vraddetailprops.h b/mp/src/utils/vrad/vraddetailprops.h new file mode 100644 index 00000000..f9cc8da1 --- /dev/null +++ b/mp/src/utils/vrad/vraddetailprops.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef VRADDETAILPROPS_H
+#define VRADDETAILPROPS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "bspfile.h"
+#include "mathlib/anorms.h"
+
+
+// Calculate the lighting at whatever surface the ray hits.
+// Note: this ADDS to the values already in color. So if you want absolute
+// values in there, then clear the values in color[] first.
+void CalcRayAmbientLighting(
+ int iThread,
+ const Vector &vStart,
+ const Vector &vEnd,
+ float tanTheta, // tangent of the inner angle of the cone
+ Vector color[MAX_LIGHTSTYLES] // The color contribution from each lightstyle.
+ );
+
+bool CastRayInLeaf( int iThread, const Vector &start, const Vector &end, int leafIndex, float *pFraction, Vector *pNormal );
+
+void ComputeDetailPropLighting( int iThread );
+
+
+#endif // VRADDETAILPROPS_H
diff --git a/mp/src/utils/vrad/vraddisps.cpp b/mp/src/utils/vrad/vraddisps.cpp new file mode 100644 index 00000000..1e8d2606 --- /dev/null +++ b/mp/src/utils/vrad/vraddisps.cpp @@ -0,0 +1,1783 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "vrad.h"
+#include "utlvector.h"
+#include "cmodel.h"
+#include "BSPTreeData.h"
+#include "VRAD_DispColl.h"
+#include "CollisionUtils.h"
+#include "lightmap.h"
+#include "Radial.h"
+#include "CollisionUtils.h"
+#include "mathlib/bumpvects.h"
+#include "utlrbtree.h"
+#include "tier0/fasttimer.h"
+#include "disp_vrad.h"
+
+class CBSPDispRayDistanceEnumerator;
+
+//=============================================================================
+//
+// Displacement/Face List
+//
+class CBSPDispFaceListEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
+{
+public:
+
+ //=========================================================================
+ //
+ // Construction/Deconstruction
+ //
+ CBSPDispFaceListEnumerator() {};
+ virtual ~CBSPDispFaceListEnumerator()
+ {
+ m_DispList.Purge();
+ m_FaceList.Purge();
+ }
+
+ // ISpatialLeafEnumerator
+ bool EnumerateLeaf( int ndxLeaf, int context );
+
+ // IBSPTreeDataEnumerator
+ bool FASTCALL EnumerateElement( int userId, int context );
+
+public:
+
+ CUtlVector<CVRADDispColl*> m_DispList;
+ CUtlVector<int> m_FaceList;
+};
+
+
+//=============================================================================
+//
+// RayEnumerator
+//
+class CBSPDispRayEnumerator : public ISpatialLeafEnumerator, public IBSPTreeDataEnumerator
+{
+public:
+ // ISpatialLeafEnumerator
+ bool EnumerateLeaf( int ndxLeaf, int context );
+
+ // IBSPTreeDataEnumerator
+ bool FASTCALL EnumerateElement( int userId, int context );
+};
+
+//=============================================================================
+//
+// VRad Displacement Manager
+//
+class CVRadDispMgr : public IVRadDispMgr
+{
+public:
+
+ //=========================================================================
+ //
+ // Construction/Deconstruction
+ //
+ CVRadDispMgr();
+ virtual ~CVRadDispMgr();
+
+ // creation/destruction
+ void Init( void );
+ void Shutdown( void );
+
+ // "CalcPoints"
+ bool BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
+ bool BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
+ bool BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace );
+
+ // patching functions
+ void MakePatches( void );
+ void SubdividePatch( int iPatch );
+
+ // pre "FinalLightFace"
+ void InsertSamplesDataIntoHashTable( void );
+ void InsertPatchSampleDataIntoHashTable( void );
+
+ // "FinalLightFace"
+ radial_t *BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump );
+ bool SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl, LightingValue_t *pLightSample, int sampleCount, bool bPatch );
+ radial_t *BuildPatchRadial( int ndxFace, bool bBump );
+
+ // utility
+ void GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal, bool bInside );
+ void GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree );
+
+ // bsp tree functions
+ bool ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray );
+ bool ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf );
+ void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray, int ndxLeaf,
+ float& dist, dface_t*& pFace, Vector2D& luxelCoord );
+ void ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, Vector *pNormal );
+
+ void StartRayTest( DispTested_t &dispTested );
+ void AddPolysForRayTrace( void );
+
+ // general timing -- should be moved!!
+ void StartTimer( const char *name );
+ void EndTimer( void );
+
+ //=========================================================================
+ //
+ // Enumeration Methods
+ //
+ bool DispRay_EnumerateLeaf( int ndxLeaf, int context );
+ bool DispRay_EnumerateElement( int userId, int context );
+ bool DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pEnum );
+
+ bool DispFaceList_EnumerateLeaf( int ndxLeaf, int context );
+ bool DispFaceList_EnumerateElement( int userId, int context );
+
+private:
+
+ //=========================================================================
+ //
+ // BSP Tree Helpers
+ //
+ void InsertDispIntoTree( int ndxDisp );
+ void RemoveDispFromTree( int ndxDisp );
+
+ //=========================================================================
+ //
+ // Displacement Data Loader (from .bsp)
+ //
+ void UnserializeDisps( void );
+ void DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace );
+
+ //=========================================================================
+ //
+ // Sampling Helpers
+ //
+ void RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial, int ndxStyle, bool bBump );
+ void RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle );
+
+ void RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial, bool bBump );
+ void RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
+ Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump,
+ CUtlVector<CPatch*> &interestingPatches );
+
+ bool IsNeighbor( int iDispFace, int iNeighborFace );
+
+ void GetInterestingPatchesForLuxels(
+ int ndxFace,
+ CUtlVector<CPatch*> &interestingPatches,
+ float patchSampleRadius );
+
+private:
+
+ struct DispCollTree_t
+ {
+ CVRADDispColl *m_pDispTree;
+ BSPTreeDataHandle_t m_Handle;
+ };
+
+ struct EnumContext_t
+ {
+ DispTested_t *m_pDispTested;
+ Ray_t const *m_pRay;
+ };
+
+ CUtlVector<DispCollTree_t> m_DispTrees;
+
+ IBSPTreeData *m_pBSPTreeData;
+
+ CBSPDispRayEnumerator m_EnumDispRay;
+ CBSPDispFaceListEnumerator m_EnumDispFaceList;
+
+ int sampleCount;
+ Vector *m_pSamplePos;
+
+ CFastTimer m_Timer;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: expose IVRadDispMgr to vrad
+//-----------------------------------------------------------------------------
+
+static CVRadDispMgr s_DispMgr;
+
+IVRadDispMgr *StaticDispMgr( void )
+{
+ return &s_DispMgr;
+}
+
+
+//=============================================================================
+//
+// Displacement/Face List
+//
+// ISpatialLeafEnumerator
+bool CBSPDispFaceListEnumerator::EnumerateLeaf( int ndxLeaf, int context )
+{
+ return s_DispMgr.DispFaceList_EnumerateLeaf( ndxLeaf, context );
+}
+
+// IBSPTreeDataEnumerator
+bool FASTCALL CBSPDispFaceListEnumerator::EnumerateElement( int userId, int context )
+{
+ return s_DispMgr.DispFaceList_EnumerateElement( userId, context );
+}
+
+
+//=============================================================================
+//
+// RayEnumerator
+//
+bool CBSPDispRayEnumerator::EnumerateLeaf( int ndxLeaf, int context )
+{
+ return s_DispMgr.DispRay_EnumerateLeaf( ndxLeaf, context );
+}
+
+bool FASTCALL CBSPDispRayEnumerator::EnumerateElement( int userId, int context )
+{
+ return s_DispMgr.DispRay_EnumerateElement( userId, context );
+}
+
+
+//-----------------------------------------------------------------------------
+// Here's an enumerator that we use for testing against disps in a leaf...
+//-----------------------------------------------------------------------------
+
+class CBSPDispRayDistanceEnumerator : public IBSPTreeDataEnumerator
+{
+public:
+ CBSPDispRayDistanceEnumerator() : m_Distance(1.0f), m_pSurface(0) {}
+
+ // IBSPTreeDataEnumerator
+ bool FASTCALL EnumerateElement( int userId, int context )
+ {
+ return s_DispMgr.DispRayDistance_EnumerateElement( userId, this );
+ }
+
+ float m_Distance;
+ dface_t* m_pSurface;
+ DispTested_t *m_pDispTested;
+ Ray_t const *m_pRay;
+ Vector2D m_LuxelCoord;
+ Vector m_Normal;
+};
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRadDispMgr::CVRadDispMgr()
+{
+ m_pBSPTreeData = CreateBSPTreeData();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CVRadDispMgr::~CVRadDispMgr()
+{
+ DestroyBSPTreeData( m_pBSPTreeData );
+}
+
+
+//-----------------------------------------------------------------------------
+// Insert a displacement into the tree for collision
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::InsertDispIntoTree( int ndxDisp )
+{
+ DispCollTree_t &dispTree = m_DispTrees[ndxDisp];
+ CDispCollTree *pDispTree = dispTree.m_pDispTree;
+
+ // get the bounding box of the tree
+ Vector boxMin, boxMax;
+ pDispTree->GetBounds( boxMin, boxMax );
+
+ // add the displacement to the tree so we will collide against it
+ dispTree.m_Handle = m_pBSPTreeData->Insert( ndxDisp, boxMin, boxMax );
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove a displacement from the tree for collision
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RemoveDispFromTree( int ndxDisp )
+{
+ // release the tree handle
+ if( m_DispTrees[ndxDisp].m_Handle != TREEDATA_INVALID_HANDLE )
+ {
+ m_pBSPTreeData->Remove( m_DispTrees[ndxDisp].m_Handle );
+ m_DispTrees[ndxDisp].m_Handle = TREEDATA_INVALID_HANDLE;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::Init( void )
+{
+ // initialize the bsp tree
+ m_pBSPTreeData->Init( ToolBSPTree() );
+
+ // read in displacements that have been compiled into the bsp file
+ UnserializeDisps();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::Shutdown( void )
+{
+ // remove all displacements from the tree
+ for( int ndxDisp = m_DispTrees.Size(); ndxDisp >= 0; ndxDisp-- )
+ {
+ RemoveDispFromTree( ndxDisp );
+ }
+
+ // shutdown the bsp tree
+ m_pBSPTreeData->Shutdown();
+
+ // purge the displacement collision tree list
+ m_DispTrees.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::DispBuilderInit( CCoreDispInfo *pBuilderDisp, dface_t *pFace, int ndxFace )
+{
+ // get the .bsp displacement
+ ddispinfo_t *pDisp = &g_dispinfo[pFace->dispinfo];
+ if( !pDisp )
+ return;
+
+ //
+ // initlialize the displacement base surface
+ //
+ CCoreDispSurface *pSurf = pBuilderDisp->GetSurface();
+ pSurf->SetPointCount( 4 );
+ pSurf->SetHandle( ndxFace );
+ pSurf->SetContents( pDisp->contents );
+
+ Vector pt[4];
+ int ndxPt;
+ for( ndxPt = 0; ndxPt < 4; ndxPt++ )
+ {
+ int eIndex = dsurfedges[pFace->firstedge+ndxPt];
+ if( eIndex < 0 )
+ {
+ pSurf->SetPoint( ndxPt, dvertexes[dedges[-eIndex].v[1]].point );
+ }
+ else
+ {
+ pSurf->SetPoint( ndxPt, dvertexes[dedges[eIndex].v[0]].point );
+ }
+
+ VectorCopy( pSurf->GetPoint(ndxPt), pt[ndxPt] );
+ }
+
+ //
+ // calculate the displacement surface normal
+ //
+ Vector vFaceNormal;
+ pSurf->GetNormal( vFaceNormal );
+ for( ndxPt = 0; ndxPt < 4; ndxPt++ )
+ {
+ pSurf->SetPointNormal( ndxPt, vFaceNormal );
+ }
+
+ // set the surface initial point info
+ pSurf->SetPointStart( pDisp->startPosition );
+ pSurf->FindSurfPointStartIndex();
+ pSurf->AdjustSurfPointData();
+
+ Vector vecTmp( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
+ int nLuxelsPerWorldUnit = static_cast<int>( 1.0f / VectorLength( vecTmp ) );
+ Vector vecU( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][0],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][1],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[0][2] );
+ Vector vecV( texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][0],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][1],
+ texinfo[pFace->texinfo].lightmapVecsLuxelsPerWorldUnits[1][2] );
+ pSurf->CalcLuxelCoords( nLuxelsPerWorldUnit, false, vecU, vecV );
+
+ pBuilderDisp->SetNeighborData( pDisp->m_EdgeNeighbors, pDisp->m_CornerNeighbors );
+
+ CDispVert *pVerts = &g_DispVerts[ pDisp->m_iDispVertStart ];
+ CDispTri *pTris = &g_DispTris[pDisp->m_iDispTriStart];
+
+ //
+ // initialize the displacement data
+ //
+ pBuilderDisp->InitDispInfo(
+ pDisp->power,
+ pDisp->minTess,
+ pDisp->smoothingAngle,
+ pVerts,
+ pTris );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::UnserializeDisps( void )
+{
+ // temporarily create the "builder" displacements
+ CUtlVector<CCoreDispInfo*> builderDisps;
+ for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
+ {
+ CCoreDispInfo *pDisp = new CCoreDispInfo;
+ if ( !pDisp )
+ {
+ builderDisps.Purge();
+ return;
+ }
+
+ int nIndex = builderDisps.AddToTail();
+ pDisp->SetListIndex( nIndex );
+ builderDisps[nIndex] = pDisp;
+ }
+
+ // Set them up as CDispUtilsHelpers.
+ for ( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
+ {
+ builderDisps[iDisp]->SetDispUtilsHelperInfo( builderDisps.Base(), g_dispinfo.Count() );
+ }
+
+ //
+ // find all faces with displacement data and initialize
+ //
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ dface_t *pFace = &g_pFaces[ndxFace];
+ if( ValidDispFace( pFace ) )
+ {
+ DispBuilderInit( builderDisps[pFace->dispinfo], pFace, ndxFace );
+ }
+ }
+
+ // generate the displacement surfaces
+ for( int iDisp = 0; iDisp < g_dispinfo.Count(); ++iDisp )
+ {
+ builderDisps[iDisp]->Create();
+ }
+
+ // smooth edge normals
+ SmoothNeighboringDispSurfNormals( builderDisps.Base(), g_dispinfo.Count() );
+
+ //
+ // create the displacement collision tree and add it to the bsp tree
+ //
+ CVRADDispColl *pDispTrees = new CVRADDispColl[g_dispinfo.Count()];
+ if( !pDispTrees )
+ return;
+
+ m_DispTrees.AddMultipleToTail( g_dispinfo.Count() );
+
+ for( int iDisp = 0; iDisp < g_dispinfo.Count(); iDisp++ )
+ {
+ pDispTrees[iDisp].Create( builderDisps[iDisp] );
+
+ m_DispTrees[iDisp].m_pDispTree = &pDispTrees[iDisp];
+ m_DispTrees[iDisp].m_Handle = TREEDATA_INVALID_HANDLE;
+
+ InsertDispIntoTree( iDisp );
+ }
+
+ // free "builder" disps
+ builderDisps.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: create a set of patches for each displacement surface to transfer
+// bounced light around with
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::MakePatches( void )
+{
+ // Collect stats - keep track of the total displacement surface area.
+ float flTotalArea = 0.0f;
+
+ // Create patches for all of the displacements.
+ int nTreeCount = m_DispTrees.Size();
+ for( int iTree = 0; iTree < nTreeCount; ++iTree )
+ {
+ // Get the current displacement collision tree.
+ CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
+ if( !pDispTree )
+ continue;
+
+ flTotalArea += pDispTree->CreateParentPatches();
+ }
+
+ // Print stats.
+ qprintf( "%i Displacements\n", nTreeCount );
+ qprintf( "%i Square Feet [%.2f Square Inches]\n", ( int )( flTotalArea / 144.0f ), flTotalArea );
+}
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::SubdividePatch( int iPatch )
+{
+ // Get the current patch to subdivide.
+ CPatch *pPatch = &g_Patches[iPatch];
+ if ( !pPatch )
+ return;
+
+ // Create children patches.
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[pPatch->faceNumber].dispinfo];
+ CVRADDispColl *pTree = dispTree.m_pDispTree;
+ if( pTree )
+ {
+ pTree->CreateChildPatches( iPatch, 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::StartRayTest( DispTested_t &dispTested )
+{
+ if( m_DispTrees.Size() > 0 )
+ {
+ if( dispTested.m_pTested == 0 )
+ {
+ dispTested.m_pTested = new int[m_DispTrees.Size()];
+ memset( dispTested.m_pTested, 0, m_DispTrees.Size() * sizeof( int ) );
+ dispTested.m_Enum = 0;
+ }
+ ++dispTested.m_Enum;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::ClipRayToDisp( DispTested_t &dispTested, Ray_t const &ray )
+{
+ StartRayTest( dispTested );
+
+ EnumContext_t ctx;
+ ctx.m_pRay = &ray;
+ ctx.m_pDispTested = &dispTested;
+
+ // If it got through without a hit, it returns true
+ return !m_pBSPTreeData->EnumerateLeavesAlongRay( ray, &m_EnumDispRay, ( int )&ctx );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf )
+{
+ EnumContext_t ctx;
+ ctx.m_pRay = &ray;
+ ctx.m_pDispTested = &dispTested;
+
+ return !m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, ( int )&ctx );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, dface_t*& pFace, Vector2D& luxelCoord )
+{
+ CBSPDispRayDistanceEnumerator rayTestEnum;
+ rayTestEnum.m_pRay = &ray;
+ rayTestEnum.m_pDispTested = &dispTested;
+
+ m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
+
+ dist = rayTestEnum.m_Distance;
+ pFace = rayTestEnum.m_pSurface;
+ if (pFace)
+ {
+ Vector2DCopy( rayTestEnum.m_LuxelCoord, luxelCoord );
+ }
+}
+
+void CVRadDispMgr::ClipRayToDispInLeaf( DispTested_t &dispTested, Ray_t const &ray,
+ int ndxLeaf, float& dist, Vector *pNormal )
+{
+ CBSPDispRayDistanceEnumerator rayTestEnum;
+ rayTestEnum.m_pRay = &ray;
+ rayTestEnum.m_pDispTested = &dispTested;
+
+ m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &rayTestEnum, 0 );
+ dist = rayTestEnum.m_Distance;
+ if ( rayTestEnum.m_pSurface )
+ {
+ *pNormal = rayTestEnum.m_Normal;
+ }
+}
+
+void CVRadDispMgr::AddPolysForRayTrace( void )
+{
+ int nTreeCount = m_DispTrees.Size();
+ for( int iTree = 0; iTree < nTreeCount; ++iTree )
+ {
+ // Get the current displacement collision tree.
+ CVRADDispColl *pDispTree = m_DispTrees[iTree].m_pDispTree;
+
+ // Add the triangles of the tree to the RT environment
+ pDispTree->AddPolysForRayTrace();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::GetDispSurfNormal( int ndxFace, Vector &pt, Vector &ptNormal,
+ bool bInside )
+{
+ // get the displacement surface data
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+
+ // find the parameterized displacement indices
+ Vector2D uv;
+ pDispTree->BaseFacePlaneToDispUV( pt, uv );
+
+ if( bInside )
+ {
+ if( uv[0] < 0.0f || uv[0] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[0] ); }
+ if( uv[1] < 0.0f || uv[1] > 1.0f ) { Msg( "Disp UV (%f) outside bounds!\n", uv[1] ); }
+ }
+
+ if( uv[0] < 0.0f ) { uv[0] = 0.0f; }
+ if( uv[0] > 1.0f ) { uv[0] = 1.0f; }
+ if( uv[1] < 0.0f ) { uv[1] = 0.0f; }
+ if( uv[1] > 1.0f ) { uv[1] = 1.0f; }
+
+ // get the normal at "pt"
+ pDispTree->DispUVToSurfNormal( uv, ptNormal );
+
+ // get the new "pt"
+ pDispTree->DispUVToSurfPoint( uv, pt, 1.0f );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::GetDispSurf( int ndxFace, CVRADDispColl **ppDispTree )
+{
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ *ppDispTree = dispTree.m_pDispTree;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispRay_EnumerateLeaf( int ndxLeaf, int context )
+{
+ return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispRay, context );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispRay_EnumerateElement( int userId, int context )
+{
+ DispCollTree_t &dispTree = m_DispTrees[userId];
+ EnumContext_t *pCtx = ( EnumContext_t* )context;
+
+ // don't test twice (check tested value)
+ if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
+ return true;
+
+ // set the tested value
+ pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
+
+ // false mean stop iterating -- return false if we hit! (NOTE: opposite return
+ // result of the collision tree's ray test, thus the !)
+ CBaseTrace trace;
+ trace.fraction = 1.0f;
+ return ( !dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, pCtx->m_pRay->InvDelta(), &trace, true ) );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+bool CVRadDispMgr::DispRayDistance_EnumerateElement( int userId, CBSPDispRayDistanceEnumerator* pCtx )
+{
+ DispCollTree_t &dispTree = m_DispTrees[userId];
+
+ // don't test twice (check tested value)
+ if( pCtx->m_pDispTested->m_pTested[userId] == pCtx->m_pDispTested->m_Enum )
+ return true;
+
+ // set the tested value
+ pCtx->m_pDispTested->m_pTested[userId] = pCtx->m_pDispTested->m_Enum;
+
+ // Test the ray, if it's closer than previous tests, use it!
+ RayDispOutput_t output;
+ output.ndxVerts[0] = -1;
+ output.ndxVerts[1] = -1;
+ output.ndxVerts[2] = -1;
+ output.ndxVerts[3] = -1;
+ output.u = -1.0f;
+ output.v = -1.0f;
+ output.dist = FLT_MAX;
+
+ if (dispTree.m_pDispTree->AABBTree_Ray( *pCtx->m_pRay, output ))
+ {
+ if (output.dist < pCtx->m_Distance)
+ {
+ pCtx->m_Distance = output.dist;
+ pCtx->m_pSurface = &g_pFaces[dispTree.m_pDispTree->GetParentIndex()];
+
+ // Get the luxel coordinate
+ ComputePointFromBarycentric(
+ dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[0]),
+ dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[1]),
+ dispTree.m_pDispTree->GetLuxelCoord(output.ndxVerts[2]),
+ output.u, output.v, pCtx->m_LuxelCoord );
+
+ Vector v0,v1,v2;
+ dispTree.m_pDispTree->GetVert( output.ndxVerts[0], v0 );
+ dispTree.m_pDispTree->GetVert( output.ndxVerts[1], v1 );
+ dispTree.m_pDispTree->GetVert( output.ndxVerts[2], v2 );
+ Vector e0 = v1-v0;
+ Vector e1 = v2-v0;
+ pCtx->m_Normal = CrossProduct( e0, e1 );
+ VectorNormalize(pCtx->m_Normal);
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Test a ray against a particular dispinfo
+//-----------------------------------------------------------------------------
+
+/*
+float CVRadDispMgr::ClipRayToDisp( Ray_t const &ray, int dispinfo )
+{
+ assert( m_DispTrees.IsValidIndex(dispinfo) );
+
+ RayDispOutput_t output;
+ if (!m_DispTrees[dispinfo].m_pDispTree->AABBTree_Ray( ray, output ))
+ return 1.0f;
+ return output.dist;
+}
+*/
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispFaceList_EnumerateLeaf( int ndxLeaf, int context )
+{
+ //
+ // add the faces found in this leaf to the face list
+ //
+ dleaf_t *pLeaf = &dleafs[ndxLeaf];
+ for( int ndxFace = 0; ndxFace < pLeaf->numleaffaces; ndxFace++ )
+ {
+ // get the current face index
+ int ndxLeafFace = pLeaf->firstleafface + ndxFace;
+
+ // check to see if the face already lives in the list
+ int ndx;
+ int size = m_EnumDispFaceList.m_FaceList.Size();
+ for( ndx = 0; ndx < size; ndx++ )
+ {
+ if( m_EnumDispFaceList.m_FaceList[ndx] == ndxLeafFace )
+ break;
+ }
+
+ if( ndx == size )
+ {
+ int ndxList = m_EnumDispFaceList.m_FaceList.AddToTail();
+ m_EnumDispFaceList.m_FaceList[ndxList] = ndxLeafFace;
+ }
+ }
+
+ return m_pBSPTreeData->EnumerateElementsInLeaf( ndxLeaf, &m_EnumDispFaceList, context );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::DispFaceList_EnumerateElement( int userId, int context )
+{
+ DispCollTree_t &dispTree = m_DispTrees[userId];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // check to see if the displacement already lives in the list
+ int ndx;
+ int size = m_EnumDispFaceList.m_DispList.Size();
+ for( ndx = 0; ndx < size; ndx++ )
+ {
+ if( m_EnumDispFaceList.m_DispList[ndx] == pDispTree )
+ break;
+ }
+
+ if( ndx == size )
+ {
+ int ndxList = m_EnumDispFaceList.m_DispList.AddToTail();
+ m_EnumDispFaceList.m_DispList[ndxList] = pDispTree;
+ }
+
+ return true;
+}
+
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+inline void GetSampleLight( facelight_t *pFaceLight, int ndxStyle, bool bBumped,
+ int ndxSample, LightingValue_t *pSampleLight )
+{
+// SampleLight[0].Init( 20.0f, 10.0f, 10.0f );
+// return;
+
+ // get sample from bumped lighting data
+ if( bBumped )
+ {
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pSampleLight[ndxBump] = pFaceLight->light[ndxStyle][ndxBump][ndxSample];
+ }
+ }
+ // just a generally lit surface
+ else
+ {
+ pSampleLight[0] = pFaceLight->light[ndxStyle][0][ndxSample];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void AddSampleLightToRadial( Vector const &samplePos, Vector const &sampleNormal,
+ LightingValue_t *pSampleLight, float sampleRadius2,
+ Vector const &luxelPos, Vector const &luxelNormal,
+ radial_t *pRadial, int ndxRadial, bool bBumped,
+ bool bNeighborBumped )
+{
+ // check normals to see if sample contributes any light at all
+ float angle = sampleNormal.Dot( luxelNormal );
+ if ( angle < 0.15f )
+ return;
+
+ // calculate the light vector
+ Vector vSegment = samplePos - luxelPos;
+
+ // get the distance to the light
+ float dist = vSegment.Length();
+ float dist2 = dist * dist;
+
+ // Check to see if the light is within the influence.
+ float influence = 1.0f - ( dist2 / ( sampleRadius2 ) );
+ if( influence <= 0.0f )
+ return;
+
+ influence *= angle;
+
+ if( bBumped )
+ {
+ if( bNeighborBumped )
+ {
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[ndxBump], influence );
+ }
+ pRadial->weight[ndxRadial] += influence;
+ }
+ else
+ {
+ influence *= 0.05f;
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pSampleLight[0], influence );
+ }
+ pRadial->weight[ndxRadial] += influence;
+ }
+ }
+ else
+ {
+ pRadial->light[0][ndxRadial].AddWeighted( pSampleLight[0], influence );
+ pRadial->weight[ndxRadial] += influence;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::IsNeighbor( int iFace, int iNeighborFace )
+{
+ if ( iFace == iNeighborFace )
+ return true;
+
+ faceneighbor_t *pFaceNeighbor = &faceneighbor[iFace];
+ for ( int iNeighbor = 0; iNeighbor < pFaceNeighbor->numneighbors; iNeighbor++ )
+ {
+ if ( pFaceNeighbor->neighbor[iNeighbor] == iNeighborFace )
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialLuxelAddSamples( int ndxFace, Vector const &luxelPt, Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump, int lightStyle )
+{
+ // calculate one over the voxel size
+ float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
+
+ //
+ // find voxel info
+ //
+ int voxelMin[3], voxelMax[3];
+ for( int axis = 0; axis < 3; axis++ )
+ {
+ voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
+ voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
+ }
+
+ SampleData_t sampleData;
+ for( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
+ {
+ for( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
+ {
+ for( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
+ {
+ sampleData.x = ndxX * 100;
+ sampleData.y = ndxY * 10;
+ sampleData.z = ndxZ;
+
+ UtlHashHandle_t handle = g_SampleHashTable.Find( sampleData );
+ if( handle != g_SampleHashTable.InvalidHandle() )
+ {
+ SampleData_t *pSampleData = &g_SampleHashTable.Element( handle );
+ int count = pSampleData->m_Samples.Count();
+ for( int ndx = 0; ndx < count; ndx++ )
+ {
+ SampleHandle_t sampleHandle = pSampleData->m_Samples.Element( ndx );
+ int ndxSample = ( sampleHandle & 0x0000ffff );
+ int ndxFaceLight = ( ( sampleHandle >> 16 ) & 0x0000ffff );
+
+ facelight_t *pFaceLight = &facelight[ndxFaceLight];
+ if( pFaceLight && IsNeighbor( ndxFace, ndxFaceLight ) )
+ {
+ //
+ // check for similar lightstyles
+ //
+ dface_t *pFace = &g_pFaces[ndxFaceLight];
+ if( pFace )
+ {
+ int ndxNeighborStyle = -1;
+ for( int ndxLightStyle = 0; ndxLightStyle < MAXLIGHTMAPS; ndxLightStyle++ )
+ {
+ if( pFace->styles[ndxLightStyle] == lightStyle )
+ {
+ ndxNeighborStyle = ndxLightStyle;
+ break;
+ }
+ }
+ if( ndxNeighborStyle == -1 )
+ continue;
+
+ // is this surface bumped???
+ bool bNeighborBump = texinfo[pFace->texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ LightingValue_t sampleLight[NUM_BUMP_VECTS+1];
+ GetSampleLight( pFaceLight, ndxNeighborStyle, bNeighborBump, ndxSample, sampleLight );
+ AddSampleLightToRadial( pFaceLight->sample[ndxSample].pos, pFaceLight->sample[ndxSample].normal,
+ sampleLight, radius*radius, luxelPt, luxelNormal, pRadial, ndxRadial,
+ bBump, bNeighborBump );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialLuxelBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
+ int ndxStyle, bool bBump )
+{
+ //
+ // get data lighting data
+ //
+ int ndxFace = pDispTree->GetParentIndex();
+
+ dface_t *pFace = &g_pFaces[ndxFace];
+ facelight_t *pFaceLight = &facelight[ndxFace];
+
+ // get the influence radius
+ float radius2 = pDispTree->GetSampleRadius2();
+ float radius = ( float )sqrt( radius2 );
+
+ int radialSize = pRadial->w * pRadial->h;
+ for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
+ {
+ RadialLuxelAddSamples( ndxFace, pFaceLight->luxel[ndxRadial], pFaceLight->luxelNormals[ndxRadial],
+ radius, pRadial, ndxRadial, bBump, pFace->styles[ndxStyle] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+radial_t *CVRadDispMgr::BuildLuxelRadial( int ndxFace, int ndxStyle, bool bBump )
+{
+ // allocate the radial
+ radial_t *pRadial = AllocateRadial( ndxFace );
+ if( !pRadial )
+ return NULL;
+
+ //
+ // step 1: get the displacement surface to be lit
+ //
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return NULL;
+
+ // step 2: build radial luxels
+ RadialLuxelBuild( pDispTree, pRadial, ndxStyle, bBump );
+
+ // step 3: return the built radial
+ return pRadial;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::SampleRadial( int ndxFace, radial_t *pRadial, Vector const &vPos, int ndxLxl,
+ LightingValue_t *pLightSample, int sampleCount, bool bPatch )
+{
+ bool bGoodSample = true;
+ for ( int count = 0; count < sampleCount; count++ )
+ {
+ pLightSample[count].Zero();
+
+ if ( pRadial->weight[ndxLxl] > 0.0f )
+ {
+ pLightSample[count].AddWeighted( pRadial->light[count][ndxLxl], ( 1.0f / pRadial->weight[ndxLxl] ) );
+ }
+ else
+ {
+ // error, luxel has no samples (not for patches)
+ if ( !bPatch )
+ {
+ // Yes, 2550 is correct!
+ // pLightSample[count].Init( 2550.0f, 0.0f, 2550.0f );
+ if( count == 0 )
+ bGoodSample = false;
+ }
+ }
+ }
+
+ return bGoodSample;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void GetPatchLight( CPatch *pPatch, bool bBump, Vector *pPatchLight )
+{
+ VectorCopy( pPatch->totallight.light[0], pPatchLight[0] );
+
+ if( bBump )
+ {
+ for( int ndxBump = 1; ndxBump < ( NUM_BUMP_VECTS + 1 ); ndxBump++ )
+ {
+ VectorCopy( pPatch->totallight.light[ndxBump], pPatchLight[ndxBump] );
+ }
+ }
+}
+
+extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal,
+ const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] );
+extern void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal );
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void AddPatchLightToRadial( Vector const &patchOrigin, Vector const &patchNormal,
+ Vector *pPatchLight, float patchRadius2,
+ Vector const &luxelPos, Vector const &luxelNormal,
+ radial_t *pRadial, int ndxRadial, bool bBump,
+ bool bNeighborBump )
+{
+ // calculate the light vector
+ Vector vSegment = patchOrigin - luxelPos;
+
+ // get the distance to the light
+ float dist = vSegment.Length();
+ float dist2 = dist * dist;
+
+ // Check to see if the light is within the sample influence.
+ float influence = 1.0f - ( dist2 / ( patchRadius2 ) );
+ if ( influence <= 0.0f )
+ return;
+
+ if( bBump )
+ {
+ Vector normals[NUM_BUMP_VECTS+1];
+ normals[0] = luxelNormal;
+ texinfo_t *pTexinfo = &texinfo[g_pFaces[pRadial->facenum].texinfo];
+ Vector vecTexU, vecTexV;
+ PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] );
+ GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] );
+
+ if( bNeighborBump )
+ {
+ float flScale = patchNormal.Dot( normals[0] );
+ flScale = clamp( flScale, 0.0f, flScale );
+ float flBumpInfluence = influence * flScale;
+
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[ndxBump], flBumpInfluence );
+ }
+
+ pRadial->weight[ndxRadial] += flBumpInfluence;
+ }
+ else
+ {
+ float flScale = patchNormal.Dot( normals[0] );
+ flScale = clamp( flScale, 0.0f, flScale );
+ float flBumpInfluence = influence * flScale * 0.05f;
+
+ for( int ndxBump = 0; ndxBump < ( NUM_BUMP_VECTS+1 ); ndxBump++ )
+ {
+ pRadial->light[ndxBump][ndxRadial].AddWeighted( pPatchLight[0], flBumpInfluence );
+ }
+
+ pRadial->weight[ndxRadial] += flBumpInfluence;
+ }
+ }
+ else
+ {
+ float flScale = patchNormal.Dot( luxelNormal );
+ flScale = clamp( flScale, 0.0f, flScale );
+ influence *= flScale;
+ pRadial->light[0][ndxRadial].AddWeighted( pPatchLight[0], influence );
+
+ // add the weight value
+ pRadial->weight[ndxRadial] += influence;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialLuxelAddPatch( int ndxFace, Vector const &luxelPt,
+ Vector const &luxelNormal, float radius,
+ radial_t *pRadial, int ndxRadial, bool bBump,
+ CUtlVector<CPatch*> &interestingPatches )
+{
+#ifdef SAMPLEHASH_QUERY_ONCE
+ for ( int i=0; i < interestingPatches.Count(); i++ )
+ {
+ CPatch *pPatch = interestingPatches[i];
+ bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ Vector patchLight[NUM_BUMP_VECTS+1];
+ GetPatchLight( pPatch, bBump, patchLight );
+ AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
+ luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
+ }
+#else
+ // calculate one over the voxel size
+ float ooVoxelSize = 1.0f / SAMPLEHASH_VOXEL_SIZE;
+
+ //
+ // find voxel info
+ //
+ int voxelMin[3], voxelMax[3];
+ for ( int axis = 0; axis < 3; axis++ )
+ {
+ voxelMin[axis] = ( int )( ( luxelPt[axis] - radius ) * ooVoxelSize );
+ voxelMax[axis] = ( int )( ( luxelPt[axis] + radius ) * ooVoxelSize ) + 1;
+ }
+
+ unsigned short curIterationKey = IncrementPatchIterationKey();
+ PatchSampleData_t patchData;
+ for ( int ndxZ = voxelMin[2]; ndxZ < voxelMax[2] + 1; ndxZ++ )
+ {
+ for ( int ndxY = voxelMin[1]; ndxY < voxelMax[1] + 1; ndxY++ )
+ {
+ for ( int ndxX = voxelMin[0]; ndxX < voxelMax[0] + 1; ndxX++ )
+ {
+ patchData.x = ndxX * 100;
+ patchData.y = ndxY * 10;
+ patchData.z = ndxZ;
+
+ UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
+ if ( handle != g_PatchSampleHashTable.InvalidHandle() )
+ {
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+ int count = pPatchData->m_ndxPatches.Count();
+ for ( int ndx = 0; ndx < count; ndx++ )
+ {
+ int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
+ CPatch *pPatch = &g_Patches.Element( ndxPatch );
+ if ( pPatch && pPatch->m_IterationKey != curIterationKey )
+ {
+ pPatch->m_IterationKey = curIterationKey;
+
+ if ( IsNeighbor( ndxFace, pPatch->faceNumber ) )
+ {
+ bool bNeighborBump = texinfo[g_pFaces[pPatch->faceNumber].texinfo].flags & SURF_BUMPLIGHT ? true : false;
+
+ Vector patchLight[NUM_BUMP_VECTS+1];
+ GetPatchLight( pPatch, bBump, patchLight );
+ AddPatchLightToRadial( pPatch->origin, pPatch->normal, patchLight, radius*radius,
+ luxelPt, luxelNormal, pRadial, ndxRadial, bBump, bNeighborBump );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+#endif
+}
+
+
+void CVRadDispMgr::GetInterestingPatchesForLuxels(
+ int ndxFace,
+ CUtlVector<CPatch*> &interestingPatches,
+ float patchSampleRadius )
+{
+ facelight_t *pFaceLight = &facelight[ndxFace];
+
+ // Get the max bounds of all voxels that these luxels touch.
+ Vector vLuxelMin( FLT_MAX, FLT_MAX, FLT_MAX );
+ Vector vLuxelMax( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+ for ( int i=0; i < pFaceLight->numluxels; i++ )
+ {
+ VectorMin( pFaceLight->luxel[i], vLuxelMin, vLuxelMin );
+ VectorMax( pFaceLight->luxel[i], vLuxelMax, vLuxelMax );
+ }
+
+ int allVoxelMin[3], allVoxelMax[3];
+ for ( int axis = 0; axis < 3; axis++ )
+ {
+ allVoxelMin[axis] = ( int )( ( vLuxelMin[axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
+ allVoxelMax[axis] = ( int )( ( vLuxelMax[axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
+ }
+ int allVoxelSize[3] = { allVoxelMax[0] - allVoxelMin[0], allVoxelMax[1] - allVoxelMin[1], allVoxelMax[2] - allVoxelMin[2] };
+
+
+ // Now figure out exactly which voxels these luxels touch.
+ CUtlVector<unsigned char> voxelBits;
+ voxelBits.SetSize( ((allVoxelSize[0] * allVoxelSize[1] * allVoxelSize[2]) + 7) / 8 );
+ memset( voxelBits.Base(), 0, voxelBits.Count() );
+
+ for ( int i=0; i < pFaceLight->numluxels; i++ )
+ {
+ int voxelMin[3], voxelMax[3];
+ for ( int axis=0; axis < 3; axis++ )
+ {
+ voxelMin[axis] = ( int )( ( pFaceLight->luxel[i][axis] - patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE );
+ voxelMax[axis] = ( int )( ( pFaceLight->luxel[i][axis] + patchSampleRadius ) / SAMPLEHASH_VOXEL_SIZE ) + 1;
+ }
+
+ for ( int x=voxelMin[0]; x < voxelMax[0]; x++ )
+ {
+ for ( int y=voxelMin[1]; y < voxelMax[1]; y++ )
+ {
+ for ( int z=voxelMin[2]; z < voxelMax[2]; z++ )
+ {
+ int iBit = (z - allVoxelMin[2])*(allVoxelSize[0]*allVoxelSize[1]) +
+ (y-allVoxelMin[1])*allVoxelSize[0] +
+ (x-allVoxelMin[0]);
+ voxelBits[iBit>>3] |= (1 << (iBit & 7));
+ }
+ }
+ }
+ }
+
+
+ // Now get the list of patches that touch those voxels.
+ unsigned short curIterationKey = IncrementPatchIterationKey();
+
+ for ( int x=0; x < allVoxelSize[0]; x++ )
+ {
+ for ( int y=0; y < allVoxelSize[1]; y++ )
+ {
+ for ( int z=0; z < allVoxelSize[2]; z++ )
+ {
+ // Make sure this voxel has any luxels that care about it.
+ int iBit = z*(allVoxelSize[0]*allVoxelSize[1]) + y*allVoxelSize[0] + x;
+ unsigned char val = voxelBits[iBit>>3] & (1 << (iBit & 7));
+ if ( !val )
+ continue;
+
+ PatchSampleData_t patchData;
+ patchData.x = (x + allVoxelMin[0]) * 100;
+ patchData.y = (y + allVoxelMin[1]) * 10;
+ patchData.z = (z + allVoxelMin[2]);
+
+ UtlHashHandle_t handle = g_PatchSampleHashTable.Find( patchData );
+ if ( handle != g_PatchSampleHashTable.InvalidHandle() )
+ {
+ PatchSampleData_t *pPatchData = &g_PatchSampleHashTable.Element( handle );
+
+ // For all patches that touch this hash table element..
+ for ( int ndx = 0; ndx < pPatchData->m_ndxPatches.Count(); ndx++ )
+ {
+ int ndxPatch = pPatchData->m_ndxPatches.Element( ndx );
+ CPatch *pPatch = &g_Patches.Element( ndxPatch );
+
+ // If we haven't touched the patch already and it's a valid neighbor, then we want to use it.
+ if ( pPatch && pPatch->m_IterationKey != curIterationKey )
+ {
+ pPatch->m_IterationKey = curIterationKey;
+
+ if ( IsNeighbor( ndxFace, pPatch->faceNumber ) )
+ {
+ interestingPatches.AddToTail( pPatch );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::RadialPatchBuild( CVRADDispColl *pDispTree, radial_t *pRadial,
+ bool bBump )
+{
+ //
+ // get data lighting data
+ //
+ int ndxFace = pDispTree->GetParentIndex();
+ facelight_t *pFaceLight = &facelight[ndxFace];
+
+ // get the influence radius
+ float radius2 = pDispTree->GetPatchSampleRadius2();
+ float radius = ( float )sqrt( radius2 );
+
+ CUtlVector<CPatch*> interestingPatches;
+#ifdef SAMPLEHASH_QUERY_ONCE
+ GetInterestingPatchesForLuxels( ndxFace, interestingPatches, radius );
+#endif
+
+ int radialSize = pRadial->w * pRadial->h;
+ for( int ndxRadial = 0; ndxRadial < radialSize; ndxRadial++ )
+ {
+ RadialLuxelAddPatch(
+ ndxFace,
+ pFaceLight->luxel[ndxRadial],
+ pFaceLight->luxelNormals[ndxRadial],
+ radius,
+ pRadial,
+ ndxRadial,
+ bBump,
+ interestingPatches );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+radial_t *CVRadDispMgr::BuildPatchRadial( int ndxFace, bool bBump )
+{
+ // allocate the radial
+ radial_t *pRadial = AllocateRadial( ndxFace );
+ if( !pRadial )
+ return NULL;
+
+ //
+ // step 1: get the displacement surface to be lit
+ //
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return NULL;
+
+ // step 2: build radial of patch light
+ RadialPatchBuild( pDispTree, pRadial, bBump );
+
+ // step 3: return the built radial
+ return pRadial;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool SampleInSolid( sample_t *pSample )
+{
+ int ndxLeaf = PointLeafnum( pSample->pos );
+ return ( dleafs[ndxLeaf].contents == CONTENTS_SOLID );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::InsertSamplesDataIntoHashTable( void )
+{
+ int totalSamples = 0;
+#if 0
+ int totalSamplesInSolid = 0;
+#endif
+
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ dface_t *pFace = &g_pFaces[ndxFace];
+ facelight_t *pFaceLight = &facelight[ndxFace];
+ if( !pFace || !pFaceLight )
+ continue;
+
+ if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
+ continue;
+
+#if 0
+ bool bDisp = ( pFace->dispinfo != -1 );
+#endif
+ //
+ // for each sample
+ //
+ for( int ndxSample = 0; ndxSample < pFaceLight->numsamples; ndxSample++ )
+ {
+ sample_t *pSample = &pFaceLight->sample[ndxSample];
+ if( pSample )
+ {
+#if 0
+ if( bDisp )
+ {
+ // test sample to see if the displacement samples resides in solid
+ if( SampleInSolid( pSample ) )
+ {
+ totalSamplesInSolid++;
+ continue;
+ }
+ }
+#endif
+
+ // create the sample handle
+ SampleHandle_t sampleHandle = ndxSample;
+ sampleHandle |= ( ndxFace << 16 );
+
+ SampleData_AddSample( pSample, sampleHandle );
+ }
+
+ }
+
+ totalSamples += pFaceLight->numsamples;
+ }
+
+#if 0
+ // not implemented yet!!!
+ Msg( "%d samples in solid\n", totalSamplesInSolid );
+#endif
+
+ // log the distribution
+ SampleData_Log();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::InsertPatchSampleDataIntoHashTable( void )
+{
+ // don't insert patch samples if we are not bouncing light
+ if( numbounce <= 0 )
+ return;
+
+ int totalPatchSamples = 0;
+
+ for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
+ {
+ dface_t *pFace = &g_pFaces[ndxFace];
+ facelight_t *pFaceLight = &facelight[ndxFace];
+ if( !pFace || !pFaceLight )
+ continue;
+
+ if( texinfo[pFace->texinfo].flags & TEX_SPECIAL )
+ continue;
+
+ //
+ // for each patch
+ //
+ CPatch *pNextPatch = NULL;
+ if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
+ {
+ for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
+ {
+ // next patch
+ pNextPatch = NULL;
+ if( pPatch->ndxNext != g_Patches.InvalidIndex() )
+ {
+ pNextPatch = &g_Patches.Element( pPatch->ndxNext );
+ }
+
+ // skip patches with children
+ if( pPatch->child1 != g_Patches.InvalidIndex() )
+ continue;
+
+ int ndxPatch = pPatch - g_Patches.Base();
+ PatchSampleData_AddSample( pPatch, ndxPatch );
+
+ totalPatchSamples++;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::StartTimer( const char *name )
+{
+ Msg( name );
+ m_Timer.Start();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CVRadDispMgr::EndTimer( void )
+{
+ m_Timer.End();
+ CCycleCount duration = m_Timer.GetDuration();
+ double seconds = duration.GetSeconds();
+
+ Msg( "Done<%1.4lf sec>\n", seconds );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::BuildDispSamples( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // get the tree assosciated with the face
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calculate the steps in uv space
+ float stepU = 1.0f / ( float )width;
+ float stepV = 1.0f / ( float )height;
+ float halfStepU = stepU * 0.5f;
+ float halfStepV = stepV * 0.5f;
+
+ //
+ // build the winding points (used to generate world space winding and
+ // calculate the area of the "sample")
+ //
+ int ndxU, ndxV;
+
+ // NOTE: These allocations are necessary to avoid stack overflow
+ // FIXME: Solve with storing global per-thread temp buffers if there's
+ // a performance problem with this solution...
+ bool bTempAllocationNecessary = ((height + 1) * (width + 1)) > SINGLE_BRUSH_MAP;
+
+ Vector worldPointBuffer[SINGLE_BRUSH_MAP];
+ sample_t sampleBuffer[SINGLE_BRUSH_MAP*2];
+
+ Vector *pWorldPoints = worldPointBuffer;
+ sample_t *pSamples = sampleBuffer;
+ if (bTempAllocationNecessary)
+ {
+ pWorldPoints = new Vector[ SINGLEMAP ];
+ pSamples = new sample_t[ SINGLEMAP ];
+ }
+
+ for( ndxV = 0; ndxV < ( height + 1 ); ndxV++ )
+ {
+ for( ndxU = 0; ndxU < ( width + 1 ); ndxU++ )
+ {
+ int ndx = ( ndxV * ( width + 1 ) ) + ndxU;
+
+ Vector2D uv( ndxU * stepU, ndxV * stepV );
+ pDispTree->DispUVToSurfPoint( uv, pWorldPoints[ndx], 0.0f );
+ }
+ }
+
+
+ for( ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( ndxU = 0; ndxU < width; ndxU++ )
+ {
+ // build the winding
+ winding_t *pWinding = AllocWinding( 4 );
+ if( pWinding )
+ {
+ pWinding->numpoints = 4;
+ pWinding->p[0] = pWorldPoints[(ndxV*(width+1))+ndxU];
+ pWinding->p[1] = pWorldPoints[((ndxV+1)*(width+1))+ndxU];
+ pWinding->p[2] = pWorldPoints[((ndxV+1)*(width+1))+(ndxU+1)];
+ pWinding->p[3] = pWorldPoints[(ndxV*(width+1))+(ndxU+1)];
+
+ // calculate the area
+ float area = WindingArea( pWinding );
+
+ int ndxSample = ( ndxV * width ) + ndxU;
+ pSamples[ndxSample].w = pWinding;
+ pSamples[ndxSample].area = area;
+ }
+ else
+ {
+ Msg( "BuildDispSamples: WARNING - failed winding allocation\n" );
+ }
+ }
+ }
+
+ //
+ // build the samples points (based on s, t and sampleoffset (center of samples);
+ // generates world space position and normal)
+ //
+ for( ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( ndxU = 0; ndxU < width; ndxU++ )
+ {
+ int ndxSample = ( ndxV * width ) + ndxU;
+ pSamples[ndxSample].s = ndxU;
+ pSamples[ndxSample].t = ndxV;
+ pSamples[ndxSample].coord[0] = ( ndxU * stepU ) + halfStepU;
+ pSamples[ndxSample].coord[1] = ( ndxV * stepV ) + halfStepV;
+ pDispTree->DispUVToSurfPoint( pSamples[ndxSample].coord, pSamples[ndxSample].pos, 1.0f );
+ pDispTree->DispUVToSurfNormal( pSamples[ndxSample].coord, pSamples[ndxSample].normal );
+ }
+ }
+
+ //
+ // copy over samples
+ //
+ pFaceLight->numsamples = width * height;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ goto buildDispSamplesError;
+
+ memcpy( pFaceLight->sample, pSamples, pFaceLight->numsamples * sizeof( *pFaceLight->sample ) );
+
+ // statistics - warning?!
+ if( pFaceLight->numsamples == 0 )
+ {
+ Msg( "BuildDispSamples: WARNING - no samples %d\n", pLightInfo->face - g_pFaces );
+ }
+
+ if (bTempAllocationNecessary)
+ {
+ delete[] pWorldPoints;
+ delete[] pSamples;
+ }
+
+ return true;
+
+buildDispSamplesError:
+ if (bTempAllocationNecessary)
+ {
+ delete[] pWorldPoints;
+ delete[] pSamples;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::BuildDispLuxels( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // get the tree assosciated with the face
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calcuate actual luxel points
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
+ if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
+ return false;
+
+ float stepU = 1.0f / ( float )( width - 1 );
+ float stepV = 1.0f / ( float )( height - 1 );
+
+ for( int ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( int ndxU = 0; ndxU < width; ndxU++ )
+ {
+ int ndxLuxel = ( ndxV * width ) + ndxU;
+
+ Vector2D uv( ndxU * stepU, ndxV * stepV );
+ pDispTree->DispUVToSurfPoint( uv, pFaceLight->luxel[ndxLuxel], 1.0f );
+ pDispTree->DispUVToSurfNormal( uv, pFaceLight->luxelNormals[ndxLuxel] );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CVRadDispMgr::BuildDispSamplesAndLuxels_DoFast( lightinfo_t *pLightInfo, facelight_t *pFaceLight, int ndxFace )
+{
+ // get the tree assosciated with the face
+ DispCollTree_t &dispTree = m_DispTrees[g_pFaces[ndxFace].dispinfo];
+ CVRADDispColl *pDispTree = dispTree.m_pDispTree;
+ if( !pDispTree )
+ return false;
+
+ // lightmap size
+ int width = pLightInfo->face->m_LightmapTextureSizeInLuxels[0]+1;
+ int height = pLightInfo->face->m_LightmapTextureSizeInLuxels[1]+1;
+
+ // calcuate actual luxel points
+ pFaceLight->numsamples = width * height;
+ pFaceLight->sample = ( sample_t* )calloc( pFaceLight->numsamples, sizeof( *pFaceLight->sample ) );
+ if( !pFaceLight->sample )
+ return false;
+
+ pFaceLight->numluxels = width * height;
+ pFaceLight->luxel = ( Vector* )calloc( pFaceLight->numluxels, sizeof( *pFaceLight->luxel ) );
+ pFaceLight->luxelNormals = ( Vector* )calloc( pFaceLight->numluxels, sizeof( Vector ) );
+ if( !pFaceLight->luxel || !pFaceLight->luxelNormals )
+ return false;
+
+ float stepU = 1.0f / ( float )( width - 1 );
+ float stepV = 1.0f / ( float )( height - 1 );
+ float halfStepU = stepU * 0.5f;
+ float halfStepV = stepV * 0.5f;
+
+ for( int ndxV = 0; ndxV < height; ndxV++ )
+ {
+ for( int ndxU = 0; ndxU < width; ndxU++ )
+ {
+ int ndx = ( ndxV * width ) + ndxU;
+
+ pFaceLight->sample[ndx].s = ndxU;
+ pFaceLight->sample[ndx].t = ndxV;
+ pFaceLight->sample[ndx].coord[0] = ( ndxU * stepU ) + halfStepU;
+ pFaceLight->sample[ndx].coord[1] = ( ndxV * stepV ) + halfStepV;
+
+ pDispTree->DispUVToSurfPoint( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].pos, 1.0f );
+ pDispTree->DispUVToSurfNormal( pFaceLight->sample[ndx].coord, pFaceLight->sample[ndx].normal );
+
+ pFaceLight->luxel[ndx] = pFaceLight->sample[ndx].pos;
+ pFaceLight->luxelNormals[ndx] = pFaceLight->sample[ndx].normal;
+ }
+ }
+
+ return true;
+}
diff --git a/mp/src/utils/vrad/vraddll.cpp b/mp/src/utils/vrad/vraddll.cpp new file mode 100644 index 00000000..87f8d7fc --- /dev/null +++ b/mp/src/utils/vrad/vraddll.cpp @@ -0,0 +1,243 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+//#include <strstrea.h>
+#include "vraddll.h"
+#include "bsplib.h"
+#include "vrad.h"
+#include "map_shared.h"
+#include "lightmap.h"
+#include "threads.h"
+
+
+static CUtlVector<unsigned char> g_LastGoodLightData;
+static CUtlVector<unsigned char> g_FacesTouched;
+
+
+static CVRadDLL g_VRadDLL;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVRadDLL, IVRadDLL, VRAD_INTERFACE_VERSION, g_VRadDLL );
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CVRadDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION, g_VRadDLL );
+
+
+// ---------------------------------------------------------------------------- //
+// temporary static array data size tracking
+// original data size = 143 megs
+// - converting ddispindices, ddispverts, g_dispinfo, and dlightdata to CUtlVector
+// - 51 megs
+// ---------------------------------------------------------------------------- //
+
+class dat
+{
+public:
+ char *name;
+ int size;
+};
+#define DATENTRY(name) {#name, sizeof(name)}
+
+dat g_Dats[] =
+{
+ DATENTRY(dmodels),
+ DATENTRY(dvisdata),
+ DATENTRY(dlightdataLDR),
+ DATENTRY(dlightdataHDR),
+ DATENTRY(dentdata),
+ DATENTRY(dleafs),
+ DATENTRY(dplanes),
+ DATENTRY(dvertexes),
+ DATENTRY(g_vertnormalindices),
+ DATENTRY(g_vertnormals),
+ DATENTRY(texinfo),
+ DATENTRY(dtexdata),
+ DATENTRY(g_dispinfo),
+ DATENTRY(dorigfaces),
+ DATENTRY(g_primitives),
+ DATENTRY(g_primverts),
+ DATENTRY(g_primindices),
+ DATENTRY(dfaces),
+ DATENTRY(dedges),
+ DATENTRY(dleaffaces),
+ DATENTRY(dleafbrushes),
+ DATENTRY(dsurfedges),
+ DATENTRY(dbrushes),
+ DATENTRY(dbrushsides),
+ DATENTRY(dareas),
+ DATENTRY(dareaportals),
+ DATENTRY(dworldlights),
+ DATENTRY(dleafwaterdata),
+ DATENTRY(g_ClipPortalVerts),
+ DATENTRY(g_CubemapSamples),
+ DATENTRY(g_TexDataStringData),
+ DATENTRY(g_TexDataStringTable),
+ DATENTRY(g_Overlays)
+};
+
+int CalcDatSize()
+{
+ int ret = 0;
+ int count = sizeof( g_Dats ) / sizeof( g_Dats[0] );
+
+ int i;
+ for( i=1; i < count; i++ )
+ {
+ if( g_Dats[i-1].size > g_Dats[i].size )
+ {
+ dat temp = g_Dats[i-1];
+ g_Dats[i-1] = g_Dats[i];
+ g_Dats[i] = temp;
+
+ if( i > 1 )
+ i -= 2;
+ else
+ i -= 1;
+ }
+ }
+
+ for( i=0; i < count; i++ )
+ ret += g_Dats[i].size;
+
+ return ret;
+}
+
+int g_TotalDatSize = CalcDatSize();
+
+
+
+
+int CVRadDLL::main( int argc, char **argv )
+{
+ return VRAD_Main( argc, argv );
+}
+
+
+bool CVRadDLL::Init( char const *pFilename )
+{
+ VRAD_Init();
+
+ // Set options and run vrad startup code.
+ do_fast = true;
+ g_bLowPriorityThreads = true;
+ g_pIncremental = GetIncremental();
+
+ VRAD_LoadBSP( pFilename );
+ return true;
+}
+
+
+void CVRadDLL::Release()
+{
+}
+
+
+void CVRadDLL::GetBSPInfo( CBSPInfo *pInfo )
+{
+ pInfo->dlightdata = pdlightdata->Base();
+ pInfo->lightdatasize = pdlightdata->Count();
+
+ pInfo->dfaces = dfaces;
+ pInfo->m_pFacesTouched = g_FacesTouched.Base();
+ pInfo->numfaces = numfaces;
+
+ pInfo->dvertexes = dvertexes;
+ pInfo->numvertexes = numvertexes;
+
+ pInfo->dedges = dedges;
+ pInfo->numedges = numedges;
+
+ pInfo->dsurfedges = dsurfedges;
+ pInfo->numsurfedges = numsurfedges;
+
+ pInfo->texinfo = texinfo.Base();
+ pInfo->numtexinfo = texinfo.Count();
+
+ pInfo->g_dispinfo = g_dispinfo.Base();
+ pInfo->g_numdispinfo = g_dispinfo.Count();
+
+ pInfo->dtexdata = dtexdata;
+ pInfo->numtexdata = numtexdata;
+
+ pInfo->texDataStringData = g_TexDataStringData.Base();
+ pInfo->nTexDataStringData = g_TexDataStringData.Count();
+
+ pInfo->texDataStringTable = g_TexDataStringTable.Base();
+ pInfo->nTexDataStringTable = g_TexDataStringTable.Count();
+}
+
+
+bool CVRadDLL::DoIncrementalLight( char const *pVMFFile )
+{
+ char tempPath[MAX_PATH], tempFilename[MAX_PATH];
+ GetTempPath( sizeof( tempPath ), tempPath );
+ GetTempFileName( tempPath, "vmf_entities_", 0, tempFilename );
+
+ FileHandle_t fp = g_pFileSystem->Open( tempFilename, "wb" );
+ if( !fp )
+ return false;
+
+ g_pFileSystem->Write( pVMFFile, strlen(pVMFFile)+1, fp );
+ g_pFileSystem->Close( fp );
+
+ // Parse the new entities.
+ if( !LoadEntsFromMapFile( tempFilename ) )
+ return false;
+
+ // Create lights.
+ CreateDirectLights();
+
+ // set up sky cameras
+ ProcessSkyCameras();
+
+ g_bInterrupt = false;
+ if( RadWorld_Go() )
+ {
+ // Save off the last finished lighting results for the BSP.
+ g_LastGoodLightData.CopyArray( pdlightdata->Base(), pdlightdata->Count() );
+ if( g_pIncremental )
+ g_pIncremental->GetFacesTouched( g_FacesTouched );
+
+ return true;
+ }
+ else
+ {
+ g_iCurFace = 0;
+ return false;
+ }
+}
+
+
+bool CVRadDLL::Serialize()
+{
+ if( !g_pIncremental )
+ return false;
+
+ if( g_LastGoodLightData.Count() > 0 )
+ {
+ pdlightdata->CopyArray( g_LastGoodLightData.Base(), g_LastGoodLightData.Count() );
+
+ if( g_pIncremental->Serialize() )
+ {
+ // Delete this so it doesn't keep re-saving it.
+ g_LastGoodLightData.Purge();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+float CVRadDLL::GetPercentComplete()
+{
+ return (float)g_iCurFace / numfaces;
+}
+
+
+void CVRadDLL::Interrupt()
+{
+ g_bInterrupt = true;
+}
+
+
diff --git a/mp/src/utils/vrad/vraddll.h b/mp/src/utils/vrad/vraddll.h new file mode 100644 index 00000000..506e8eb6 --- /dev/null +++ b/mp/src/utils/vrad/vraddll.h @@ -0,0 +1,34 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef VRADDLL_H
+#define VRADDLL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "ivraddll.h"
+#include "ilaunchabledll.h"
+
+
+class CVRadDLL : public IVRadDLL, public ILaunchableDLL
+{
+// IVRadDLL overrides.
+public:
+ virtual int main( int argc, char **argv );
+ virtual bool Init( char const *pFilename );
+ virtual void Release();
+ virtual void GetBSPInfo( CBSPInfo *pInfo );
+ virtual bool DoIncrementalLight( char const *pVMFFile );
+ virtual bool Serialize();
+ virtual float GetPercentComplete();
+ virtual void Interrupt();
+};
+
+
+#endif // VRADDLL_H
diff --git a/mp/src/utils/vrad/vradstaticprops.cpp b/mp/src/utils/vrad/vradstaticprops.cpp new file mode 100644 index 00000000..f240d94f --- /dev/null +++ b/mp/src/utils/vrad/vradstaticprops.cpp @@ -0,0 +1,1915 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Revision: $
+// $NoKeywords: $
+//
+// This file contains code to allow us to associate client data with bsp leaves.
+//
+//=============================================================================//
+
+#include "vrad.h"
+#include "mathlib/vector.h"
+#include "UtlBuffer.h"
+#include "utlvector.h"
+#include "GameBSPFile.h"
+#include "BSPTreeData.h"
+#include "VPhysics_Interface.h"
+#include "Studio.h"
+#include "Optimize.h"
+#include "Bsplib.h"
+#include "CModel.h"
+#include "PhysDll.h"
+#include "phyfile.h"
+#include "collisionutils.h"
+#include "tier1/KeyValues.h"
+#include "pacifier.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/hardwareverts.h"
+#include "byteswap.h"
+#include "mpivrad.h"
+#include "vtf/vtf.h"
+#include "tier1/utldict.h"
+#include "tier1/utlsymbol.h"
+
+#include "messbuf.h"
+#include "vmpi.h"
+#include "vmpi_distribute_work.h"
+
+
+#define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1))
+
+// identifies a vertex embedded in solid
+// lighting will be copied from nearest valid neighbor
+struct badVertex_t
+{
+ int m_ColorVertex;
+ Vector m_Position;
+ Vector m_Normal;
+};
+
+// a final colored vertex
+struct colorVertex_t
+{
+ Vector m_Color;
+ Vector m_Position;
+ bool m_bValid;
+};
+
+class CComputeStaticPropLightingResults
+{
+public:
+ ~CComputeStaticPropLightingResults()
+ {
+ m_ColorVertsArrays.PurgeAndDeleteElements();
+ }
+
+ CUtlVector< CUtlVector<colorVertex_t>* > m_ColorVertsArrays;
+};
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+CUtlSymbolTable g_ForcedTextureShadowsModels;
+
+// DON'T USE THIS FROM WITHIN A THREAD. THERE IS A THREAD CONTEXT CREATED
+// INSIDE PropTested_t. USE THAT INSTEAD.
+IPhysicsCollision *s_pPhysCollision = NULL;
+
+//-----------------------------------------------------------------------------
+// Vrad's static prop manager
+//-----------------------------------------------------------------------------
+
+class CVradStaticPropMgr : public IVradStaticPropMgr
+{
+public:
+ // constructor, destructor
+ CVradStaticPropMgr();
+ virtual ~CVradStaticPropMgr();
+
+ // methods of IStaticPropMgr
+ void Init();
+ void Shutdown();
+
+ // iterate all the instanced static props and compute their vertex lighting
+ void ComputeLighting( int iThread );
+
+private:
+ // VMPI stuff.
+ static void VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf );
+ static void VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker );
+ void VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf );
+ void VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker );
+
+ // local thread version
+ static void ThreadComputeStaticPropLighting( int iThread, void *pUserData );
+ void ComputeLightingForProp( int iThread, int iStaticProp );
+
+ // Methods associated with unserializing static props
+ void UnserializeModelDict( CUtlBuffer& buf );
+ void UnserializeModels( CUtlBuffer& buf );
+ void UnserializeStaticProps();
+
+ // Creates a collision model
+ void CreateCollisionModel( char const* pModelName );
+
+private:
+ // Unique static prop models
+ struct StaticPropDict_t
+ {
+ vcollide_t m_loadedModel;
+ CPhysCollide* m_pModel;
+ Vector m_Mins; // Bounding box is in local coordinates
+ Vector m_Maxs;
+ studiohdr_t* m_pStudioHdr;
+ CUtlBuffer m_VtxBuf;
+ CUtlVector<int> m_textureShadowIndex; // each texture has an index if this model casts texture shadows
+ CUtlVector<int> m_triangleMaterialIndex;// each triangle has an index if this model casts texture shadows
+ };
+
+ struct MeshData_t
+ {
+ CUtlVector<Vector> m_Verts;
+ int m_nLod;
+ };
+
+ // A static prop instance
+ struct CStaticProp
+ {
+ Vector m_Origin;
+ QAngle m_Angles;
+ Vector m_mins;
+ Vector m_maxs;
+ Vector m_LightingOrigin;
+ int m_ModelIdx;
+ BSPTreeDataHandle_t m_Handle;
+ CUtlVector<MeshData_t> m_MeshData;
+ int m_Flags;
+ bool m_bLightingOriginValid;
+ };
+
+ // Enumeration context
+ struct EnumContext_t
+ {
+ PropTested_t* m_pPropTested;
+ Ray_t const* m_pRay;
+ };
+
+ // The list of all static props
+ CUtlVector <StaticPropDict_t> m_StaticPropDict;
+ CUtlVector <CStaticProp> m_StaticProps;
+
+ bool m_bIgnoreStaticPropTrace;
+
+ void ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults );
+ void ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults );
+
+ void SerializeLighting();
+ void AddPolysForRayTrace();
+ void BuildTriList( CStaticProp &prop );
+};
+
+
+//-----------------------------------------------------------------------------
+// Expose IVradStaticPropMgr to vrad
+//-----------------------------------------------------------------------------
+
+static CVradStaticPropMgr g_StaticPropMgr;
+IVradStaticPropMgr* StaticPropMgr()
+{
+ return &g_StaticPropMgr;
+}
+
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+
+CVradStaticPropMgr::CVradStaticPropMgr()
+{
+ // set to ignore static prop traces
+ m_bIgnoreStaticPropTrace = false;
+}
+
+CVradStaticPropMgr::~CVradStaticPropMgr()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Makes sure the studio model is a static prop
+//-----------------------------------------------------------------------------
+
+bool IsStaticProp( studiohdr_t* pHdr )
+{
+ if (!(pHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP))
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Load a file into a Utlbuf
+//-----------------------------------------------------------------------------
+static bool LoadFile( char const* pFileName, CUtlBuffer& buf )
+{
+ if ( !g_pFullFileSystem )
+ return false;
+
+ return g_pFullFileSystem->ReadFile( pFileName, NULL, buf );
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructs the file name from the model name
+//-----------------------------------------------------------------------------
+static char const* ConstructFileName( char const* pModelName )
+{
+ static char buf[1024];
+ sprintf( buf, "%s%s", gamedir, pModelName );
+ return buf;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from a studio mesh
+//-----------------------------------------------------------------------------
+static CPhysConvex* ComputeConvexHull( mstudiomesh_t* pMesh, studiohdr_t *pStudioHdr )
+{
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ // Generate a list of all verts in the mesh
+ Vector** ppVerts = (Vector**)_alloca(pMesh->numvertices * sizeof(Vector*) );
+ for (int i = 0; i < pMesh->numvertices; ++i)
+ {
+ ppVerts[i] = vertData->Position(i);
+ }
+
+ // Generate a convex hull from the verts
+ return s_pPhysCollision->ConvexFromVerts( ppVerts, pMesh->numvertices );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a convex hull from the studio model
+//-----------------------------------------------------------------------------
+CPhysCollide* ComputeConvexHull( studiohdr_t* pStudioHdr )
+{
+ CUtlVector<CPhysConvex*> convexHulls;
+
+ for (int body = 0; body < pStudioHdr->numbodyparts; ++body )
+ {
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( body );
+ for( int model = 0; model < pBodyPart->nummodels; ++model )
+ {
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
+ for( int mesh = 0; mesh < pStudioModel->nummeshes; ++mesh )
+ {
+ // Make a convex hull for each mesh
+ // NOTE: This won't work unless the model has been compiled
+ // with $staticprop
+ mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( mesh );
+ convexHulls.AddToTail( ComputeConvexHull( pStudioMesh, pStudioHdr ) );
+ }
+ }
+ }
+
+ // Convert an array of convex elements to a compiled collision model
+ // (this deletes the convex elements)
+ return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Load studio model vertex data from a file...
+//-----------------------------------------------------------------------------
+
+bool LoadStudioModel( char const* pModelName, CUtlBuffer& buf )
+{
+ // No luck, gotta build it
+ // Construct the file name...
+ if (!LoadFile( pModelName, buf ))
+ {
+ Warning("Error! Unable to load model \"%s\"\n", pModelName );
+ return false;
+ }
+
+ // Check that it's valid
+ if (strncmp ((const char *) buf.PeekGet(), "IDST", 4) &&
+ strncmp ((const char *) buf.PeekGet(), "IDAG", 4))
+ {
+ Warning("Error! Invalid model file \"%s\"\n", pModelName );
+ return false;
+ }
+
+ studiohdr_t* pHdr = (studiohdr_t*)buf.PeekGet();
+
+ Studio_ConvertStudioHdrToNewVersion( pHdr );
+
+ if (pHdr->version != STUDIO_VERSION)
+ {
+ Warning("Error! Invalid model version \"%s\"\n", pModelName );
+ return false;
+ }
+
+ if (!IsStaticProp(pHdr))
+ {
+ Warning("Error! To use model \"%s\"\n"
+ " as a static prop, it must be compiled with $staticprop!\n", pModelName );
+ return false;
+ }
+
+ // ensure reset
+ pHdr->pVertexBase = NULL;
+ pHdr->pIndexBase = NULL;
+
+ return true;
+}
+
+bool LoadStudioCollisionModel( char const* pModelName, CUtlBuffer& buf )
+{
+ char tmp[1024];
+ Q_strncpy( tmp, pModelName, sizeof( tmp ) );
+ Q_SetExtension( tmp, ".phy", sizeof( tmp ) );
+ // No luck, gotta build it
+ if (!LoadFile( tmp, buf ))
+ {
+ // this is not an error, the model simply has no PHY file
+ return false;
+ }
+
+ phyheader_t *header = (phyheader_t *)buf.PeekGet();
+
+ if ( header->size != sizeof(*header) || header->solidCount <= 0 )
+ return false;
+
+ return true;
+}
+
+bool LoadVTXFile( char const* pModelName, const studiohdr_t *pStudioHdr, CUtlBuffer& buf )
+{
+ char filename[MAX_PATH];
+
+ // construct filename
+ Q_StripExtension( pModelName, filename, sizeof( filename ) );
+ strcat( filename, ".dx80.vtx" );
+
+ if ( !LoadFile( filename, buf ) )
+ {
+ Warning( "Error! Unable to load file \"%s\"\n", filename );
+ return false;
+ }
+
+ OptimizedModel::FileHeader_t* pVtxHdr = (OptimizedModel::FileHeader_t *)buf.Base();
+
+ // Check that it's valid
+ if ( pVtxHdr->version != OPTIMIZED_MODEL_FILE_VERSION )
+ {
+ Warning( "Error! Invalid VTX file version: %d, expected %d \"%s\"\n", pVtxHdr->version, OPTIMIZED_MODEL_FILE_VERSION, filename );
+ return false;
+ }
+ if ( pVtxHdr->checkSum != pStudioHdr->checksum )
+ {
+ Warning( "Error! Invalid VTX file checksum: %d, expected %d \"%s\"\n", pVtxHdr->checkSum, pStudioHdr->checksum, filename );
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Gets a vertex position from a strip index
+//-----------------------------------------------------------------------------
+inline static Vector* PositionFromIndex( const mstudio_meshvertexdata_t *vertData, mstudiomesh_t* pMesh, OptimizedModel::StripGroupHeader_t* pStripGroup, int i )
+{
+ OptimizedModel::Vertex_t* pVert = pStripGroup->pVertex( i );
+ return vertData->Position( pVert->origMeshVertID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Writes a glview text file containing the collision surface in question
+// Input : *pCollide -
+// *pFilename -
+//-----------------------------------------------------------------------------
+void DumpCollideToGlView( vcollide_t *pCollide, const char *pFilename )
+{
+ if ( !pCollide )
+ return;
+
+ Msg("Writing %s...\n", pFilename );
+
+ FILE *fp = fopen( pFilename, "w" );
+ for (int i = 0; i < pCollide->solidCount; ++i)
+ {
+ Vector *outVerts;
+ int vertCount = s_pPhysCollision->CreateDebugMesh( pCollide->solids[i], &outVerts );
+ int triCount = vertCount / 3;
+ int vert = 0;
+
+ unsigned char r = (i & 1) * 64 + 64;
+ unsigned char g = (i & 2) * 64 + 64;
+ unsigned char b = (i & 4) * 64 + 64;
+
+ float fr = r / 255.0f;
+ float fg = g / 255.0f;
+ float fb = b / 255.0f;
+
+ for ( int i = 0; i < triCount; i++ )
+ {
+ fprintf( fp, "3\n" );
+ fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
+ outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
+ outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
+ vert++;
+ fprintf( fp, "%6.3f %6.3f %6.3f %.2f %.3f %.3f\n",
+ outVerts[vert].x, outVerts[vert].y, outVerts[vert].z, fr, fg, fb );
+ vert++;
+ }
+ s_pPhysCollision->DestroyDebugMesh( vertCount, outVerts );
+ }
+ fclose( fp );
+}
+
+
+static bool PointInTriangle( const Vector2D &p, const Vector2D &v0, const Vector2D &v1, const Vector2D &v2 )
+{
+ float coords[3];
+ GetBarycentricCoords2D( v0, v1, v2, p, coords );
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( coords[i] < 0.0f || coords[i] > 1.0f )
+ return false;
+ }
+ float sum = coords[0] + coords[1] + coords[2];
+ if ( sum > 1.0f )
+ return false;
+ return true;
+}
+
+bool LoadFileIntoBuffer( CUtlBuffer &buf, const char *pFilename )
+{
+ FileHandle_t fileHandle = g_pFileSystem->Open( pFilename, "rb" );
+ if ( !fileHandle )
+ return false;
+
+ // Get the file size
+ int texSize = g_pFileSystem->Size( fileHandle );
+ buf.EnsureCapacity( texSize );
+ int nBytesRead = g_pFileSystem->Read( buf.Base(), texSize, fileHandle );
+ g_pFileSystem->Close( fileHandle );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
+ return true;
+}
+
+// keeps a list of all textures that cast shadows via alpha channel
+class CShadowTextureList
+{
+public:
+ // This loads a vtf and converts it to RGB8888 format
+ unsigned char *LoadVTFRGB8888( const char *pName, int *pWidth, int *pHeight, bool *pClampU, bool *pClampV )
+ {
+ char szPath[MAX_PATH];
+ Q_strncpy( szPath, "materials/", sizeof( szPath ) );
+ Q_strncat( szPath, pName, sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_strncat( szPath, ".vtf", sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
+
+ CUtlBuffer buf;
+ if ( !LoadFileIntoBuffer( buf, szPath ) )
+ return NULL;
+ IVTFTexture *pTex = CreateVTFTexture();
+ if (!pTex->Unserialize( buf ))
+ return NULL;
+ Msg("Loaded alpha texture %s\n", szPath );
+ unsigned char *pSrcImage = pTex->ImageData( 0, 0, 0, 0, 0, 0 );
+ int iWidth = pTex->Width();
+ int iHeight = pTex->Height();
+ ImageFormat dstFormat = IMAGE_FORMAT_RGBA8888;
+ ImageFormat srcFormat = pTex->Format();
+ *pClampU = (pTex->Flags() & TEXTUREFLAGS_CLAMPS) ? true : false;
+ *pClampV = (pTex->Flags() & TEXTUREFLAGS_CLAMPT) ? true : false;
+ unsigned char *pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, dstFormat, false )];
+
+ if( !ImageLoader::ConvertImageFormat( pSrcImage, srcFormat,
+ pDstImage, dstFormat, iWidth, iHeight, 0, 0 ) )
+ {
+ delete[] pDstImage;
+ return NULL;
+ }
+
+ *pWidth = iWidth;
+ *pHeight = iHeight;
+ return pDstImage;
+ }
+
+ // Checks the database for the material and loads if necessary
+ // returns true if found and pIndex will be the index, -1 if no alpha shadows
+ bool FindOrLoadIfValid( const char *pMaterialName, int *pIndex )
+ {
+ *pIndex = -1;
+ int index = m_Textures.Find(pMaterialName);
+ bool bFound = false;
+ if ( index != m_Textures.InvalidIndex() )
+ {
+ bFound = true;
+ *pIndex = index;
+ }
+ else
+ {
+ KeyValues *pVMT = new KeyValues("vmt");
+ CUtlBuffer buf(0,0,CUtlBuffer::TEXT_BUFFER);
+ LoadFileIntoBuffer( buf, pMaterialName );
+ if ( pVMT->LoadFromBuffer( pMaterialName, buf ) )
+ {
+ bFound = true;
+ if ( pVMT->FindKey("$translucent") || pVMT->FindKey("$alphatest") )
+ {
+ KeyValues *pBaseTexture = pVMT->FindKey("$basetexture");
+ if ( pBaseTexture )
+ {
+ const char *pBaseTextureName = pBaseTexture->GetString();
+ if ( pBaseTextureName )
+ {
+ int w, h;
+ bool bClampU = false;
+ bool bClampV = false;
+ unsigned char *pImageBits = LoadVTFRGB8888( pBaseTextureName, &w, &h, &bClampU, &bClampV );
+ if ( pImageBits )
+ {
+ int index = m_Textures.Insert( pMaterialName );
+ m_Textures[index].InitFromRGB8888( w, h, pImageBits );
+ *pIndex = index;
+ if ( pVMT->FindKey("$nocull") )
+ {
+ // UNDONE: Support this? Do we need to emit two triangles?
+ m_Textures[index].allowBackface = true;
+ }
+ m_Textures[index].clampU = bClampU;
+ m_Textures[index].clampV = bClampV;
+ delete[] pImageBits;
+ }
+ }
+ }
+ }
+
+ }
+ pVMT->deleteThis();
+ }
+
+ return bFound;
+ }
+
+
+ // iterate the textures for the model and load each one into the database
+ // this is used on models marked to cast texture shadows
+ void LoadAllTexturesForModel( studiohdr_t *pHdr, int *pTextureList )
+ {
+ for ( int i = 0; i < pHdr->numtextures; i++ )
+ {
+ int textureIndex = -1;
+ // try to add each texture to the transparent shadow manager
+ char szPath[MAX_PATH];
+
+ // iterate quietly through all specified directories until a valid material is found
+ for ( int j = 0; j < pHdr->numcdtextures; j++ )
+ {
+ Q_strncpy( szPath, "materials/", sizeof( szPath ) );
+ Q_strncat( szPath, pHdr->pCdtexture( j ), sizeof( szPath ) );
+ const char *textureName = pHdr->pTexture( i )->pszName();
+ Q_strncat( szPath, textureName, sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_strncat( szPath, ".vmt", sizeof( szPath ), COPY_ALL_CHARACTERS );
+ Q_FixSlashes( szPath, CORRECT_PATH_SEPARATOR );
+ if ( FindOrLoadIfValid( szPath, &textureIndex ) )
+ break;
+ }
+
+ pTextureList[i] = textureIndex;
+ }
+ }
+
+ int AddMaterialEntry( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
+ {
+ int index = m_MaterialEntries.AddToTail();
+ m_MaterialEntries[index].textureIndex = shadowTextureIndex;
+ m_MaterialEntries[index].uv[0] = t0;
+ m_MaterialEntries[index].uv[1] = t1;
+ m_MaterialEntries[index].uv[2] = t2;
+ return index;
+ }
+
+ // HACKHACK: Compute the average coverage for this triangle by sampling the AABB of its texture space
+ float ComputeCoverageForTriangle( int shadowTextureIndex, const Vector2D &t0, const Vector2D &t1, const Vector2D &t2 )
+ {
+ float umin = min(t0.x, t1.x);
+ umin = min(umin, t2.x);
+ float umax = max(t0.x, t1.x);
+ umax = max(umax, t2.x);
+
+ float vmin = min(t0.y, t1.y);
+ vmin = min(vmin, t2.y);
+ float vmax = max(t0.y, t1.y);
+ vmax = max(vmax, t2.y);
+
+ // UNDONE: Do something about tiling
+ umin = clamp(umin, 0, 1);
+ umax = clamp(umax, 0, 1);
+ vmin = clamp(vmin, 0, 1);
+ vmax = clamp(vmax, 0, 1);
+ Assert(umin>=0.0f && umax <= 1.0f);
+ Assert(vmin>=0.0f && vmax <= 1.0f);
+ const alphatexture_t &tex = m_Textures.Element(shadowTextureIndex);
+ int u0 = umin * (tex.width-1);
+ int u1 = umax * (tex.width-1);
+ int v0 = vmin * (tex.height-1);
+ int v1 = vmax * (tex.height-1);
+
+ int total = 0;
+ int count = 0;
+ for ( int v = v0; v <= v1; v++ )
+ {
+ int row = (v * tex.width);
+ for ( int u = u0; u <= u1; u++ )
+ {
+ total += tex.pAlphaTexels[row + u];
+ count++;
+ }
+ }
+ if ( count )
+ {
+ float coverage = float(total) / (count * 255.0f);
+ return coverage;
+ }
+ return 1.0f;
+ }
+
+ int SampleMaterial( int materialIndex, const Vector &coords, bool bBackface )
+ {
+ const materialentry_t &mat = m_MaterialEntries[materialIndex];
+ const alphatexture_t &tex = m_Textures.Element(m_MaterialEntries[materialIndex].textureIndex);
+ if ( bBackface && !tex.allowBackface )
+ return 0;
+ Vector2D uv = coords.x * mat.uv[0] + coords.y * mat.uv[1] + coords.z * mat.uv[2];
+ int u = RoundFloatToInt( uv[0] * tex.width );
+ int v = RoundFloatToInt( uv[1] * tex.height );
+
+ // asume power of 2, clamp or wrap
+ // UNDONE: Support clamp? This code should work
+#if 0
+ u = tex.clampU ? clamp(u,0,(tex.width-1)) : (u & (tex.width-1));
+ v = tex.clampV ? clamp(v,0,(tex.height-1)) : (v & (tex.height-1));
+#else
+ // for now always wrap
+ u &= (tex.width-1);
+ v &= (tex.height-1);
+#endif
+
+ return tex.pAlphaTexels[v * tex.width + u];
+ }
+
+ struct alphatexture_t
+ {
+ short width;
+ short height;
+ bool allowBackface;
+ bool clampU;
+ bool clampV;
+ unsigned char *pAlphaTexels;
+
+ void InitFromRGB8888( int w, int h, unsigned char *pTexels )
+ {
+ width = w;
+ height = h;
+ pAlphaTexels = new unsigned char[w*h];
+ for ( int i = 0; i < h; i++ )
+ {
+ for ( int j = 0; j < w; j++ )
+ {
+ int index = (i*w) + j;
+ pAlphaTexels[index] = pTexels[index*4 + 3];
+ }
+ }
+ }
+ };
+ struct materialentry_t
+ {
+ int textureIndex;
+ Vector2D uv[3];
+ };
+ // this is the list of textures we've loaded
+ // only load each one once
+ CUtlDict< alphatexture_t, unsigned short > m_Textures;
+ CUtlVector<materialentry_t> m_MaterialEntries;
+};
+
+// global to keep the shadow-casting texture list and their alpha bits
+CShadowTextureList g_ShadowTextureList;
+
+float ComputeCoverageFromTexture( float b0, float b1, float b2, int32 hitID )
+{
+ const float alphaScale = 1.0f / 255.0f;
+ // UNDONE: Pass ray down to determine backfacing?
+ //Vector normal( tri.m_flNx, tri.m_flNy, tri.m_flNz );
+ //bool bBackface = DotProduct(delta, tri.N) > 0 ? true : false;
+ Vector coords(b0,b1,b2);
+ return alphaScale * g_ShadowTextureList.SampleMaterial( g_RtEnv.GetTriangleMaterial(hitID), coords, false );
+}
+
+// this is here to strip models/ or .mdl or whatnot
+void CleanModelName( const char *pModelName, char *pOutput, int outLen )
+{
+ // strip off leading models/ if it exists
+ const char *pModelDir = "models/";
+ int modelLen = Q_strlen(pModelDir);
+
+ if ( !Q_strnicmp(pModelName, pModelDir, modelLen ) )
+ {
+ pModelName += modelLen;
+ }
+ Q_strncpy( pOutput, pModelName, outLen );
+
+ // truncate any .mdl extension
+ char *dot = strchr(pOutput,'.');
+ if ( dot )
+ {
+ *dot = 0;
+ }
+
+}
+
+
+void ForceTextureShadowsOnModel( const char *pModelName )
+{
+ char buf[1024];
+ CleanModelName( pModelName, buf, sizeof(buf) );
+ if ( !g_ForcedTextureShadowsModels.Find(buf).IsValid())
+ {
+ g_ForcedTextureShadowsModels.AddString(buf);
+ }
+}
+
+bool IsModelTextureShadowsForced( const char *pModelName )
+{
+ char buf[1024];
+ CleanModelName( pModelName, buf, sizeof(buf) );
+ return g_ForcedTextureShadowsModels.Find(buf).IsValid();
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a collision model (based on the render geometry!)
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::CreateCollisionModel( char const* pModelName )
+{
+ CUtlBuffer buf;
+ CUtlBuffer bufvtx;
+ CUtlBuffer bufphy;
+
+ int i = m_StaticPropDict.AddToTail();
+ m_StaticPropDict[i].m_pModel = NULL;
+ m_StaticPropDict[i].m_pStudioHdr = NULL;
+
+ if ( !LoadStudioModel( pModelName, buf ) )
+ {
+ VectorCopy( vec3_origin, m_StaticPropDict[i].m_Mins );
+ VectorCopy( vec3_origin, m_StaticPropDict[i].m_Maxs );
+ return;
+ }
+
+ studiohdr_t* pHdr = (studiohdr_t*)buf.Base();
+
+ VectorCopy( pHdr->hull_min, m_StaticPropDict[i].m_Mins );
+ VectorCopy( pHdr->hull_max, m_StaticPropDict[i].m_Maxs );
+
+ if ( LoadStudioCollisionModel( pModelName, bufphy ) )
+ {
+ phyheader_t header;
+ bufphy.Get( &header, sizeof(header) );
+
+ vcollide_t *pCollide = &m_StaticPropDict[i].m_loadedModel;
+ s_pPhysCollision->VCollideLoad( pCollide, header.solidCount, (const char *)bufphy.PeekGet(), bufphy.TellPut() - bufphy.TellGet() );
+ m_StaticPropDict[i].m_pModel = m_StaticPropDict[i].m_loadedModel.solids[0];
+
+ /*
+ static int propNum = 0;
+ char tmp[128];
+ sprintf( tmp, "staticprop%03d.txt", propNum );
+ DumpCollideToGlView( pCollide, tmp );
+ ++propNum;
+ */
+ }
+ else
+ {
+ // mark this as unused
+ m_StaticPropDict[i].m_loadedModel.solidCount = 0;
+
+ // CPhysCollide* pPhys = CreatePhysCollide( pHdr, pVtxHdr );
+ m_StaticPropDict[i].m_pModel = ComputeConvexHull( pHdr );
+ }
+
+ // clone it
+ m_StaticPropDict[i].m_pStudioHdr = (studiohdr_t *)malloc( buf.Size() );
+ memcpy( m_StaticPropDict[i].m_pStudioHdr, (studiohdr_t*)buf.Base(), buf.Size() );
+
+ if ( !LoadVTXFile( pModelName, m_StaticPropDict[i].m_pStudioHdr, m_StaticPropDict[i].m_VtxBuf ) )
+ {
+ // failed, leave state identified as disabled
+ m_StaticPropDict[i].m_VtxBuf.Purge();
+ }
+
+ if ( g_bTextureShadows )
+ {
+ if ( (pHdr->flags & STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS) || IsModelTextureShadowsForced(pModelName) )
+ {
+ m_StaticPropDict[i].m_textureShadowIndex.RemoveAll();
+ m_StaticPropDict[i].m_triangleMaterialIndex.RemoveAll();
+ m_StaticPropDict[i].m_textureShadowIndex.AddMultipleToTail( pHdr->numtextures );
+ g_ShadowTextureList.LoadAllTexturesForModel( pHdr, m_StaticPropDict[i].m_textureShadowIndex.Base() );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialize static prop model dictionary
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::UnserializeModelDict( CUtlBuffer& buf )
+{
+ int count = buf.GetInt();
+ while ( --count >= 0 )
+ {
+ StaticPropDictLump_t lump;
+ buf.Get( &lump, sizeof(StaticPropDictLump_t) );
+
+ CreateCollisionModel( lump.m_Name );
+ }
+}
+
+void CVradStaticPropMgr::UnserializeModels( CUtlBuffer& buf )
+{
+ int count = buf.GetInt();
+
+ m_StaticProps.AddMultipleToTail(count);
+ for ( int i = 0; i < count; ++i )
+ {
+ StaticPropLump_t lump;
+ buf.Get( &lump, sizeof(StaticPropLump_t) );
+
+ VectorCopy( lump.m_Origin, m_StaticProps[i].m_Origin );
+ VectorCopy( lump.m_Angles, m_StaticProps[i].m_Angles );
+ VectorCopy( lump.m_LightingOrigin, m_StaticProps[i].m_LightingOrigin );
+ m_StaticProps[i].m_bLightingOriginValid = ( lump.m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN ) > 0;
+ m_StaticProps[i].m_ModelIdx = lump.m_PropType;
+ m_StaticProps[i].m_Handle = TREEDATA_INVALID_HANDLE;
+ m_StaticProps[i].m_Flags = lump.m_Flags;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Unserialize static props
+//-----------------------------------------------------------------------------
+
+void CVradStaticPropMgr::UnserializeStaticProps()
+{
+ // Unserialize static props, insert them into the appropriate leaves
+ GameLumpHandle_t handle = g_GameLumps.GetGameLumpHandle( GAMELUMP_STATIC_PROPS );
+ int size = g_GameLumps.GameLumpSize( handle );
+ if (!size)
+ return;
+
+ if ( g_GameLumps.GetGameLumpVersion( handle ) != GAMELUMP_STATIC_PROPS_VERSION )
+ {
+ Error( "Cannot load the static props... encountered a stale map version. Re-vbsp the map." );
+ }
+
+ if ( g_GameLumps.GetGameLump( handle ) )
+ {
+ CUtlBuffer buf( g_GameLumps.GetGameLump(handle), size, CUtlBuffer::READ_ONLY );
+ UnserializeModelDict( buf );
+
+ // Skip the leaf list data
+ int count = buf.GetInt();
+ buf.SeekGet( CUtlBuffer::SEEK_CURRENT, count * sizeof(StaticPropLeafLump_t) );
+
+ UnserializeModels( buf );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Level init, shutdown
+//-----------------------------------------------------------------------------
+
+void CVradStaticPropMgr::Init()
+{
+ CreateInterfaceFn physicsFactory = GetPhysicsFactory();
+ if ( !physicsFactory )
+ Error( "Unable to load vphysics DLL." );
+
+ s_pPhysCollision = (IPhysicsCollision *)physicsFactory( VPHYSICS_COLLISION_INTERFACE_VERSION, NULL );
+ if( !s_pPhysCollision )
+ {
+ Error( "Unable to get '%s' for physics interface.", VPHYSICS_COLLISION_INTERFACE_VERSION );
+ return;
+ }
+
+ // Read in static props that have been compiled into the bsp file
+ UnserializeStaticProps();
+}
+
+void CVradStaticPropMgr::Shutdown()
+{
+
+ // Remove all static prop model data
+ for (int i = m_StaticPropDict.Size(); --i >= 0; )
+ {
+ studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr;
+ if ( pStudioHdr )
+ {
+ if ( pStudioHdr->pVertexBase )
+ {
+ free( pStudioHdr->pVertexBase );
+ }
+ free( pStudioHdr );
+ }
+ }
+
+ m_StaticProps.Purge();
+ m_StaticPropDict.Purge();
+}
+
+void ComputeLightmapColor( dface_t* pFace, Vector &color )
+{
+ texinfo_t* pTex = &texinfo[pFace->texinfo];
+ if ( pTex->flags & SURF_SKY )
+ {
+ // sky ambient already accounted for in direct component
+ return;
+ }
+}
+
+bool PositionInSolid( Vector &position )
+{
+ int ndxLeaf = PointLeafnum( position );
+ if ( dleafs[ndxLeaf].contents & CONTENTS_SOLID )
+ {
+ // position embedded in solid
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Trace from a vertex to each direct light source, accumulating its contribution.
+//-----------------------------------------------------------------------------
+void ComputeDirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor, int iThread,
+ int static_prop_id_to_skip=-1, int nLFlags = 0)
+{
+ SSE_sampleLightOutput_t sampleOutput;
+
+ outColor.Init();
+
+ // Iterate over all direct lights and accumulate their contribution
+ int cluster = ClusterFromPoint( position );
+ for ( directlight_t *dl = activelights; dl != NULL; dl = dl->next )
+ {
+ if ( dl->light.style )
+ {
+ // skip lights with style
+ continue;
+ }
+
+ // is this lights cluster visible?
+ if ( !PVSCheck( dl->pvs, cluster ) )
+ continue;
+
+ // push the vertex towards the light to avoid surface acne
+ Vector adjusted_pos = position;
+ float flEpsilon = 0.0;
+
+ if (dl->light.type != emit_skyambient)
+ {
+ // push towards the light
+ Vector fudge;
+ if ( dl->light.type == emit_skylight )
+ fudge = -( dl->light.normal);
+ else
+ {
+ fudge = dl->light.origin-position;
+ VectorNormalize( fudge );
+ }
+ fudge *= 4.0;
+ adjusted_pos += fudge;
+ }
+ else
+ {
+ // push out along normal
+ adjusted_pos += 4.0 * normal;
+// flEpsilon = 1.0;
+ }
+
+ FourVectors adjusted_pos4;
+ FourVectors normal4;
+ adjusted_pos4.DuplicateVector( adjusted_pos );
+ normal4.DuplicateVector( normal );
+
+ GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4, 1, iThread, nLFlags | GATHERLFLAGS_FORCE_FAST,
+ static_prop_id_to_skip, flEpsilon );
+
+ VectorMA( outColor, sampleOutput.m_flFalloff.m128_f32[0] * sampleOutput.m_flDot[0].m128_f32[0], dl->light.intensity, outColor );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Takes the results from a ComputeLighting call and applies it to the static prop in question.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults )
+{
+ if ( pResults->m_ColorVertsArrays.Count() == 0 )
+ return;
+
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ Assert( pStudioHdr && pVtxHdr );
+
+ int iCurColorVertsArray = 0;
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ const CUtlVector<colorVertex_t> &colorVerts = *pResults->m_ColorVertsArrays[iCurColorVertsArray++];
+
+ for ( int nLod = 0; nLod < pVtxHdr->numLODs; nLod++ )
+ {
+ OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
+
+ for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+ {
+ mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
+ OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
+
+ for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+ {
+ OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
+ int nMeshIdx = prop.m_MeshData.AddToTail();
+ prop.m_MeshData[nMeshIdx].m_Verts.AddMultipleToTail( pStripGroup->numVerts );
+ prop.m_MeshData[nMeshIdx].m_nLod = nLod;
+
+ for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex )
+ {
+ int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID;
+
+ Assert( nIndex < pStudioModel->numvertices );
+ prop.m_MeshData[nMeshIdx].m_Verts[nVertex] = colorVerts[nIndex].m_Color;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Trace rays from each unique vertex, accumulating direct and indirect
+// sources at each ray termination. Use the winding data to distribute the unique vertexes
+// into the rendering layout.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::ComputeLighting( CStaticProp &prop, int iThread, int prop_index, CComputeStaticPropLightingResults *pResults )
+{
+ CUtlVector<badVertex_t> badVerts;
+
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ if ( !pStudioHdr || !pVtxHdr )
+ {
+ // must have model and its verts for lighting computation
+ // game will fallback to fullbright
+ return;
+ }
+
+ if (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
+ return;
+
+ VMPI_SetCurrentStage( "ComputeLighting" );
+
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ // light all unique vertexes
+ CUtlVector<colorVertex_t> *pColorVertsArray = new CUtlVector<colorVertex_t>;
+ pResults->m_ColorVertsArrays.AddToTail( pColorVertsArray );
+
+ CUtlVector<colorVertex_t> &colorVerts = *pColorVertsArray;
+ colorVerts.EnsureCount( pStudioModel->numvertices );
+ memset( colorVerts.Base(), 0, colorVerts.Count() * sizeof(colorVertex_t) );
+
+ int numVertexes = 0;
+ for ( int meshID = 0; meshID < pStudioModel->nummeshes; ++meshID )
+ {
+ mstudiomesh_t *pStudioMesh = pStudioModel->pMesh( meshID );
+ const mstudio_meshvertexdata_t *vertData = pStudioMesh->GetVertexData((void *)pStudioHdr);
+ Assert( vertData ); // This can only return NULL on X360 for now
+ for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID )
+ {
+ Vector sampleNormal;
+ Vector samplePosition;
+ // transform position and normal into world coordinate system
+ matrix3x4_t matrix;
+ AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
+ VectorTransform( *vertData->Position( vertexID ), matrix, samplePosition );
+ AngleMatrix( prop.m_Angles, matrix );
+ VectorTransform( *vertData->Normal( vertexID ), matrix, sampleNormal );
+
+ if ( PositionInSolid( samplePosition ) )
+ {
+ // vertex is in solid, add to the bad list, and recover later
+ badVertex_t badVertex;
+ badVertex.m_ColorVertex = numVertexes;
+ badVertex.m_Position = samplePosition;
+ badVertex.m_Normal = sampleNormal;
+ badVerts.AddToTail( badVertex );
+ }
+ else
+ {
+ Vector direct_pos=samplePosition;
+ int skip_prop = -1;
+ if ( g_bDisablePropSelfShadowing || ( prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) )
+ {
+ skip_prop = prop_index;
+ }
+
+ int nFlags = ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) ? GATHERLFLAGS_IGNORE_NORMALS : 0;
+
+ Vector directColor(0,0,0);
+ ComputeDirectLightingAtPoint( direct_pos,
+ sampleNormal, directColor, iThread,
+ skip_prop, nFlags );
+ Vector indirectColor(0,0,0);
+
+ if (g_bShowStaticPropNormals)
+ {
+ directColor= sampleNormal;
+ directColor += Vector(1.0,1.0,1.0);
+ directColor *= 50.0;
+ }
+ else
+ {
+ if (numbounce >= 1)
+ ComputeIndirectLightingAtPoint(
+ samplePosition, sampleNormal,
+ indirectColor, iThread, true,
+ ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS) != 0 );
+ }
+
+ colorVerts[numVertexes].m_bValid = true;
+ colorVerts[numVertexes].m_Position = samplePosition;
+ VectorAdd( directColor, indirectColor, colorVerts[numVertexes].m_Color );
+ }
+
+ numVertexes++;
+ }
+ }
+
+ // color in the bad vertexes
+ // when entire model has no lighting origin and no valid neighbors
+ // must punt, leave black coloring
+ if ( badVerts.Count() && ( prop.m_bLightingOriginValid || badVerts.Count() != numVertexes ) )
+ {
+ for ( int nBadVertex = 0; nBadVertex < badVerts.Count(); nBadVertex++ )
+ {
+ Vector bestPosition;
+ if ( prop.m_bLightingOriginValid )
+ {
+ // use the specified lighting origin
+ VectorCopy( prop.m_LightingOrigin, bestPosition );
+ }
+ else
+ {
+ // find the closest valid neighbor
+ int best = 0;
+ float closest = FLT_MAX;
+ for ( int nColorVertex = 0; nColorVertex < numVertexes; nColorVertex++ )
+ {
+ if ( !colorVerts[nColorVertex].m_bValid )
+ {
+ // skip invalid neighbors
+ continue;
+ }
+ Vector delta;
+ VectorSubtract( colorVerts[nColorVertex].m_Position, badVerts[nBadVertex].m_Position, delta );
+ float distance = VectorLength( delta );
+ if ( distance < closest )
+ {
+ closest = distance;
+ best = nColorVertex;
+ }
+ }
+
+ // use the best neighbor as the direction to crawl
+ VectorCopy( colorVerts[best].m_Position, bestPosition );
+ }
+
+ // crawl toward best position
+ // sudivide to determine a closer valid point to the bad vertex, and re-light
+ Vector midPosition;
+ int numIterations = 20;
+ while ( --numIterations > 0 )
+ {
+ VectorAdd( bestPosition, badVerts[nBadVertex].m_Position, midPosition );
+ VectorScale( midPosition, 0.5f, midPosition );
+ if ( PositionInSolid( midPosition ) )
+ break;
+ bestPosition = midPosition;
+ }
+
+ // re-light from better position
+ Vector directColor;
+ ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, directColor, iThread );
+
+ Vector indirectColor;
+ ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal,
+ indirectColor, iThread, true );
+
+ // save results, not changing valid status
+ // to ensure this offset position is not considered as a viable candidate
+ colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Position = bestPosition;
+ VectorAdd( directColor, indirectColor, colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Color );
+ }
+ }
+
+ // discard bad verts
+ badVerts.Purge();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Write the lighitng to bsp pak lump
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::SerializeLighting()
+{
+ char filename[MAX_PATH];
+ CUtlBuffer utlBuf;
+
+ // illuminate them all
+ int count = m_StaticProps.Count();
+ if ( !count )
+ {
+ // nothing to do
+ return;
+ }
+
+ char mapName[MAX_PATH];
+ Q_FileBase( source, mapName, sizeof( mapName ) );
+
+ int size;
+ for (int i = 0; i < count; ++i)
+ {
+ // no need to write this file if we didn't compute the data
+ // props marked this way will not load the info anyway
+ if ( m_StaticProps[i].m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
+ continue;
+
+ if (g_bHDR)
+ {
+ sprintf( filename, "sp_hdr_%d.vhv", i );
+ }
+ else
+ {
+ sprintf( filename, "sp_%d.vhv", i );
+ }
+
+ int totalVertexes = 0;
+ for ( int j=0; j<m_StaticProps[i].m_MeshData.Count(); j++ )
+ {
+ totalVertexes += m_StaticProps[i].m_MeshData[j].m_Verts.Count();
+ }
+
+ // allocate a buffer with enough padding for alignment
+ size = sizeof( HardwareVerts::FileHeader_t ) +
+ m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t) +
+ totalVertexes*4 + 2*512;
+ utlBuf.EnsureCapacity( size );
+ Q_memset( utlBuf.Base(), 0, size );
+
+ HardwareVerts::FileHeader_t *pVhvHdr = (HardwareVerts::FileHeader_t *)utlBuf.Base();
+
+ // align to start of vertex data
+ unsigned char *pVertexData = (unsigned char *)(sizeof( HardwareVerts::FileHeader_t ) + m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t));
+ pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
+
+ // construct header
+ pVhvHdr->m_nVersion = VHV_VERSION;
+ pVhvHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum;
+ pVhvHdr->m_nVertexFlags = VERTEX_COLOR;
+ pVhvHdr->m_nVertexSize = 4;
+ pVhvHdr->m_nVertexes = totalVertexes;
+ pVhvHdr->m_nMeshes = m_StaticProps[i].m_MeshData.Count();
+
+ for (int n=0; n<pVhvHdr->m_nMeshes; n++)
+ {
+ // construct mesh dictionary
+ HardwareVerts::MeshHeader_t *pMesh = pVhvHdr->pMesh( n );
+ pMesh->m_nLod = m_StaticProps[i].m_MeshData[n].m_nLod;
+ pMesh->m_nVertexes = m_StaticProps[i].m_MeshData[n].m_Verts.Count();
+ pMesh->m_nOffset = (unsigned int)pVertexData - (unsigned int)pVhvHdr;
+
+ // construct vertexes
+ for (int k=0; k<pMesh->m_nVertexes; k++)
+ {
+ Vector &vector = m_StaticProps[i].m_MeshData[n].m_Verts[k];
+
+ ColorRGBExp32 rgbColor;
+ VectorToColorRGBExp32( vector, rgbColor );
+ unsigned char dstColor[4];
+ ConvertRGBExp32ToRGBA8888( &rgbColor, dstColor );
+
+ // b,g,r,a order
+ pVertexData[0] = dstColor[2];
+ pVertexData[1] = dstColor[1];
+ pVertexData[2] = dstColor[0];
+ pVertexData[3] = dstColor[3];
+ pVertexData += 4;
+ }
+ }
+
+ // align to end of file
+ pVertexData = (unsigned char *)((unsigned int)pVertexData - (unsigned int)pVhvHdr);
+ pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
+
+ AddBufferToPak( GetPakFile(), filename, (void*)pVhvHdr, pVertexData - (unsigned char*)pVhvHdr, false );
+ }
+}
+
+void CVradStaticPropMgr::VMPI_ProcessStaticProp_Static( int iThread, uint64 iStaticProp, MessageBuffer *pBuf )
+{
+ g_StaticPropMgr.VMPI_ProcessStaticProp( iThread, iStaticProp, pBuf );
+}
+
+void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static( uint64 iStaticProp, MessageBuffer *pBuf, int iWorker )
+{
+ g_StaticPropMgr.VMPI_ReceiveStaticPropResults( iStaticProp, pBuf, iWorker );
+}
+
+//-----------------------------------------------------------------------------
+// Called on workers to do the computation for a static prop and send
+// it to the master.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::VMPI_ProcessStaticProp( int iThread, int iStaticProp, MessageBuffer *pBuf )
+{
+ // Compute the lighting.
+ CComputeStaticPropLightingResults results;
+ ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
+
+ VMPI_SetCurrentStage( "EncodeLightingResults" );
+
+ // Encode the results.
+ int nLists = results.m_ColorVertsArrays.Count();
+ pBuf->write( &nLists, sizeof( nLists ) );
+
+ for ( int i=0; i < nLists; i++ )
+ {
+ CUtlVector<colorVertex_t> &curList = *results.m_ColorVertsArrays[i];
+ int count = curList.Count();
+ pBuf->write( &count, sizeof( count ) );
+ pBuf->write( curList.Base(), curList.Count() * sizeof( colorVertex_t ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Called on the master when a worker finishes processing a static prop.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::VMPI_ReceiveStaticPropResults( int iStaticProp, MessageBuffer *pBuf, int iWorker )
+{
+ // Read in the results.
+ CComputeStaticPropLightingResults results;
+
+ int nLists;
+ pBuf->read( &nLists, sizeof( nLists ) );
+
+ for ( int i=0; i < nLists; i++ )
+ {
+ CUtlVector<colorVertex_t> *pList = new CUtlVector<colorVertex_t>;
+ results.m_ColorVertsArrays.AddToTail( pList );
+
+ int count;
+ pBuf->read( &count, sizeof( count ) );
+ pList->SetSize( count );
+ pBuf->read( pList->Base(), count * sizeof( colorVertex_t ) );
+ }
+
+ // Apply the results.
+ ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
+}
+
+
+void CVradStaticPropMgr::ComputeLightingForProp( int iThread, int iStaticProp )
+{
+ // Compute the lighting.
+ CComputeStaticPropLightingResults results;
+ ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
+ ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
+}
+
+void CVradStaticPropMgr::ThreadComputeStaticPropLighting( int iThread, void *pUserData )
+{
+ while (1)
+ {
+ int j = GetThreadWork ();
+ if (j == -1)
+ break;
+ CComputeStaticPropLightingResults results;
+ g_StaticPropMgr.ComputeLightingForProp( iThread, j );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Computes lighting for the static props.
+// Must be after all other surface lighting has been computed for the indirect sampling.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::ComputeLighting( int iThread )
+{
+ // illuminate them all
+ int count = m_StaticProps.Count();
+ if ( !count )
+ {
+ // nothing to do
+ return;
+ }
+
+ StartPacifier( "Computing static prop lighting : " );
+
+ // ensure any traces against us are ignored because we have no inherit lighting contribution
+ m_bIgnoreStaticPropTrace = true;
+
+ if ( g_bUseMPI )
+ {
+ // Distribute the work among the workers.
+ VMPI_SetCurrentStage( "CVradStaticPropMgr::ComputeLighting" );
+
+ DistributeWork(
+ count,
+ VMPI_DISTRIBUTEWORK_PACKETID,
+ &CVradStaticPropMgr::VMPI_ProcessStaticProp_Static,
+ &CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static );
+ }
+ else
+ {
+ RunThreadsOn(count, true, ThreadComputeStaticPropLighting);
+ }
+
+ // restore default
+ m_bIgnoreStaticPropTrace = false;
+
+ // save data to bsp
+ SerializeLighting();
+
+ EndPacifier( true );
+}
+
+//-----------------------------------------------------------------------------
+// Adds all static prop polys to the ray trace store.
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::AddPolysForRayTrace( void )
+{
+ int count = m_StaticProps.Count();
+ if ( !count )
+ {
+ // nothing to do
+ return;
+ }
+
+ // Triangle coverage of 1 (full coverage)
+ Vector fullCoverage;
+ fullCoverage.x = 1.0f;
+
+ for ( int nProp = 0; nProp < count; ++nProp )
+ {
+ CStaticProp &prop = m_StaticProps[nProp];
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+
+ if ( prop.m_Flags & STATIC_PROP_NO_SHADOW )
+ continue;
+
+ // If not using static prop polys, use AABB
+ if ( !g_bStaticPropPolys )
+ {
+ if ( dict.m_pModel )
+ {
+ VMatrix xform;
+ xform.SetupMatrixOrgAngles ( prop.m_Origin, prop.m_Angles );
+ ICollisionQuery *queryModel = s_pPhysCollision->CreateQueryModel( dict.m_pModel );
+ for ( int nConvex = 0; nConvex < queryModel->ConvexCount(); ++nConvex )
+ {
+ for ( int nTri = 0; nTri < queryModel->TriangleCount( nConvex ); ++nTri )
+ {
+ Vector verts[3];
+ queryModel->GetTriangleVerts( nConvex, nTri, verts );
+ for ( int nVert = 0; nVert < 3; ++nVert )
+ verts[nVert] = xform.VMul4x3(verts[nVert]);
+ g_RtEnv.AddTriangle ( TRACE_ID_STATICPROP | nProp, verts[0], verts[1], verts[2], fullCoverage );
+ }
+ }
+ s_pPhysCollision->DestroyQueryModel( queryModel );
+ }
+ else
+ {
+ VectorAdd ( dict.m_Mins, prop.m_Origin, prop.m_mins );
+ VectorAdd ( dict.m_Maxs, prop.m_Origin, prop.m_maxs );
+ g_RtEnv.AddAxisAlignedRectangularSolid ( TRACE_ID_STATICPROP | nProp, prop.m_mins, prop.m_maxs, fullCoverage );
+ }
+
+ continue;
+ }
+
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ if ( !pStudioHdr || !pVtxHdr )
+ {
+ // must have model and its verts for decoding triangles
+ return;
+ }
+ // only init the triangle table the first time
+ bool bInitTriangles = dict.m_triangleMaterialIndex.Count() ? false : true;
+ int triangleIndex = 0;
+
+ // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
+ // body parts -> models -> lod meshes -> strip groups -> strips
+ // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ // assuming lod 0, could iterate if required
+ int nLod = 0;
+ OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
+
+ for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+ {
+ // check if this mesh's material is in the no shadow material name list
+ mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
+ mstudiotexture_t *pTxtr=pStudioHdr->pTexture(pMesh->material);
+ //printf("mat idx=%d mat name=%s\n",pMesh->material,pTxtr->pszName());
+ bool bSkipThisMesh = false;
+ for(int check=0; check<g_NonShadowCastingMaterialStrings.Count(); check++)
+ {
+ if ( Q_stristr( pTxtr->pszName(),
+ g_NonShadowCastingMaterialStrings[check] ) )
+ {
+ //printf("skip mat name=%s\n",pTxtr->pszName());
+ bSkipThisMesh = true;
+ break;
+ }
+ }
+ if ( bSkipThisMesh)
+ continue;
+
+ int shadowTextureIndex = -1;
+ if ( dict.m_textureShadowIndex.Count() )
+ {
+ shadowTextureIndex = dict.m_textureShadowIndex[pMesh->material];
+ }
+
+
+ OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+ {
+ OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
+
+ int nStrip;
+ for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
+ {
+ OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
+
+ if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
+ {
+ for ( int i = 0; i < pStrip->numIndices; i += 3 )
+ {
+ int idx = pStrip->indexOffset + i;
+
+ unsigned short i1 = *pStripGroup->pIndex( idx );
+ unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
+ unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
+
+ int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
+ int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
+ int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
+
+ // transform position into world coordinate system
+ matrix3x4_t matrix;
+ AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
+
+ Vector position1;
+ Vector position2;
+ Vector position3;
+ VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
+ VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
+ VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
+ unsigned short flags = 0;
+ int materialIndex = -1;
+ Vector color = vec3_origin;
+ if ( shadowTextureIndex >= 0 )
+ {
+ if ( bInitTriangles )
+ {
+ // add texture space and texture index to material database
+ // now
+ float coverage = g_ShadowTextureList.ComputeCoverageForTriangle(shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
+ if ( coverage < 1.0f )
+ {
+ materialIndex = g_ShadowTextureList.AddMaterialEntry( shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
+ color.x = coverage;
+ }
+ else
+ {
+ materialIndex = -1;
+ }
+ dict.m_triangleMaterialIndex.AddToTail(materialIndex);
+ }
+ else
+ {
+ materialIndex = dict.m_triangleMaterialIndex[triangleIndex];
+ triangleIndex++;
+ }
+ if ( materialIndex >= 0 )
+ {
+ flags = FCACHETRI_TRANSPARENT;
+ }
+ }
+// printf( "\ngl 3\n" );
+// printf( "gl %6.3f %6.3f %6.3f 1 0 0\n", XYZ(position1));
+// printf( "gl %6.3f %6.3f %6.3f 0 1 0\n", XYZ(position2));
+// printf( "gl %6.3f %6.3f %6.3f 0 0 1\n", XYZ(position3));
+ g_RtEnv.AddTriangle( TRACE_ID_STATICPROP | nProp,
+ position1, position2, position3,
+ color, flags, materialIndex);
+ }
+ }
+ else
+ {
+ // all tris expected to be discrete tri lists
+ // must fixme if stripping ever occurs
+ printf( "unexpected strips found\n" );
+ Assert( 0 );
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+struct tl_tri_t
+{
+ Vector p0;
+ Vector p1;
+ Vector p2;
+ Vector n0;
+ Vector n1;
+ Vector n2;
+
+ bool operator == (const tl_tri_t &t) const
+ {
+ return ( p0 == t.p0 &&
+ p1 == t.p1 &&
+ p2 == t.p2 &&
+ n0 == t.n0 &&
+ n1 == t.n1 &&
+ n2 == t.n2 );
+ }
+};
+
+struct tl_vert_t
+{
+ Vector m_position;
+ CUtlLinkedList< tl_tri_t, int > m_triList;
+};
+
+void AddTriVertsToList( CUtlVector< tl_vert_t > &triListVerts, int vertIndex, Vector vertPosition, Vector p0, Vector p1, Vector p2, Vector n0, Vector n1, Vector n2 )
+{
+ tl_tri_t tlTri;
+
+ tlTri.p0 = p0;
+ tlTri.p1 = p1;
+ tlTri.p2 = p2;
+ tlTri.n0 = n0;
+ tlTri.n1 = n1;
+ tlTri.n2 = n2;
+
+ triListVerts.EnsureCapacity( vertIndex+1 );
+
+ triListVerts[vertIndex].m_position = vertPosition;
+
+ int index = triListVerts[vertIndex].m_triList.Find( tlTri );
+ if ( !triListVerts[vertIndex].m_triList.IsValidIndex( index ) )
+ {
+ // not in list, add to list of triangles
+ triListVerts[vertIndex].m_triList.AddToTail( tlTri );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Builds a list of tris for every vertex
+//-----------------------------------------------------------------------------
+void CVradStaticPropMgr::BuildTriList( CStaticProp &prop )
+{
+ // the generated list will consist of a list of verts
+ // each vert will have a linked list of triangles that it belongs to
+ CUtlVector< tl_vert_t > triListVerts;
+
+ StaticPropDict_t &dict = m_StaticPropDict[prop.m_ModelIdx];
+ studiohdr_t *pStudioHdr = dict.m_pStudioHdr;
+ OptimizedModel::FileHeader_t *pVtxHdr = (OptimizedModel::FileHeader_t *)dict.m_VtxBuf.Base();
+ if ( !pStudioHdr || !pVtxHdr )
+ {
+ // must have model and its verts for decoding triangles
+ return;
+ }
+
+ // meshes are deeply hierarchial, divided between three stores, follow the white rabbit
+ // body parts -> models -> lod meshes -> strip groups -> strips
+ // the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
+ for ( int bodyID = 0; bodyID < pStudioHdr->numbodyparts; ++bodyID )
+ {
+ OptimizedModel::BodyPartHeader_t* pVtxBodyPart = pVtxHdr->pBodyPart( bodyID );
+ mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyID );
+
+ for ( int modelID = 0; modelID < pBodyPart->nummodels; ++modelID )
+ {
+ OptimizedModel::ModelHeader_t* pVtxModel = pVtxBodyPart->pModel( modelID );
+ mstudiomodel_t *pStudioModel = pBodyPart->pModel( modelID );
+
+ // get the specified lod, assuming lod 0
+ int nLod = 0;
+ OptimizedModel::ModelLODHeader_t *pVtxLOD = pVtxModel->pLOD( nLod );
+
+ // must reset because each model has their own vertexes [0..n]
+ // in order for this to be monolithic for the entire prop the list must be segmented
+ triListVerts.Purge();
+
+ for ( int nMesh = 0; nMesh < pStudioModel->nummeshes; ++nMesh )
+ {
+ mstudiomesh_t* pMesh = pStudioModel->pMesh( nMesh );
+ OptimizedModel::MeshHeader_t* pVtxMesh = pVtxLOD->pMesh( nMesh );
+ const mstudio_meshvertexdata_t *vertData = pMesh->GetVertexData( (void *)pStudioHdr );
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ for ( int nGroup = 0; nGroup < pVtxMesh->numStripGroups; ++nGroup )
+ {
+ OptimizedModel::StripGroupHeader_t* pStripGroup = pVtxMesh->pStripGroup( nGroup );
+
+ int nStrip;
+ for ( nStrip = 0; nStrip < pStripGroup->numStrips; nStrip++ )
+ {
+ OptimizedModel::StripHeader_t *pStrip = pStripGroup->pStrip( nStrip );
+
+ if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
+ {
+ for ( int i = 0; i < pStrip->numIndices; i += 3 )
+ {
+ int idx = pStrip->indexOffset + i;
+
+ unsigned short i1 = *pStripGroup->pIndex( idx );
+ unsigned short i2 = *pStripGroup->pIndex( idx + 1 );
+ unsigned short i3 = *pStripGroup->pIndex( idx + 2 );
+
+ int vertex1 = pStripGroup->pVertex( i1 )->origMeshVertID;
+ int vertex2 = pStripGroup->pVertex( i2 )->origMeshVertID;
+ int vertex3 = pStripGroup->pVertex( i3 )->origMeshVertID;
+
+ // transform position into world coordinate system
+ matrix3x4_t matrix;
+ AngleMatrix( prop.m_Angles, prop.m_Origin, matrix );
+
+ Vector position1;
+ Vector position2;
+ Vector position3;
+ VectorTransform( *vertData->Position( vertex1 ), matrix, position1 );
+ VectorTransform( *vertData->Position( vertex2 ), matrix, position2 );
+ VectorTransform( *vertData->Position( vertex3 ), matrix, position3 );
+
+ Vector normal1;
+ Vector normal2;
+ Vector normal3;
+ VectorTransform( *vertData->Normal( vertex1 ), matrix, normal1 );
+ VectorTransform( *vertData->Normal( vertex2 ), matrix, normal2 );
+ VectorTransform( *vertData->Normal( vertex3 ), matrix, normal3 );
+
+ AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex1, position1, position1, position2, position3, normal1, normal2, normal3 );
+ AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex2, position2, position1, position2, position3, normal1, normal2, normal3 );
+ AddTriVertsToList( triListVerts, pMesh->vertexoffset + vertex3, position3, position1, position2, position3, normal1, normal2, normal3 );
+ }
+ }
+ else
+ {
+ // all tris expected to be discrete tri lists
+ // must fixme if stripping ever occurs
+ printf( "unexpected strips found\n" );
+ Assert( 0 );
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData )
+{
+ studiohdr_t *pActiveStudioHdr = static_cast<studiohdr_t *>(pModelData);
+ Assert( pActiveStudioHdr );
+
+ if ( pActiveStudioHdr->pVertexBase )
+ {
+ return (vertexFileHeader_t *)pActiveStudioHdr->pVertexBase;
+ }
+
+ // mandatory callback to make requested data resident
+ // load and persist the vertex file
+ char fileName[MAX_PATH];
+ strcpy( fileName, "models/" );
+ strcat( fileName, pActiveStudioHdr->pszName() );
+ Q_StripExtension( fileName, fileName, sizeof( fileName ) );
+ strcat( fileName, ".vvd" );
+
+ // load the model
+ FileHandle_t fileHandle = g_pFileSystem->Open( fileName, "rb" );
+ if ( !fileHandle )
+ {
+ Error( "Unable to load vertex data \"%s\"\n", fileName );
+ }
+
+ // Get the file size
+ int vvdSize = g_pFileSystem->Size( fileHandle );
+ if ( vvdSize == 0 )
+ {
+ g_pFileSystem->Close( fileHandle );
+ Error( "Bad size for vertex data \"%s\"\n", fileName );
+ }
+
+ vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
+ g_pFileSystem->Read( pVvdHdr, vvdSize, fileHandle );
+ g_pFileSystem->Close( fileHandle );
+
+ // check header
+ if ( pVvdHdr->id != MODEL_VERTEX_FILE_ID )
+ {
+ Error("Error Vertex File %s id %d should be %d\n", fileName, pVvdHdr->id, MODEL_VERTEX_FILE_ID);
+ }
+ if ( pVvdHdr->version != MODEL_VERTEX_FILE_VERSION )
+ {
+ Error("Error Vertex File %s version %d should be %d\n", fileName, pVvdHdr->version, MODEL_VERTEX_FILE_VERSION);
+ }
+ if ( pVvdHdr->checksum != pActiveStudioHdr->checksum )
+ {
+ Error("Error Vertex File %s checksum %d should be %d\n", fileName, pVvdHdr->checksum, pActiveStudioHdr->checksum);
+ }
+
+ // need to perform mesh relocation fixups
+ // allocate a new copy
+ vertexFileHeader_t *pNewVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
+ if ( !pNewVvdHdr )
+ {
+ Error( "Error allocating %d bytes for Vertex File '%s'\n", vvdSize, fileName );
+ }
+
+ // load vertexes and run fixups
+ Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true );
+
+ // discard original
+ free( pVvdHdr );
+ pVvdHdr = pNewVvdHdr;
+
+ pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
+ return pVvdHdr;
+}
diff --git a/mp/src/utils/vrad_launcher/stdafx.cpp b/mp/src/utils/vrad_launcher/stdafx.cpp new file mode 100644 index 00000000..ecf65582 --- /dev/null +++ b/mp/src/utils/vrad_launcher/stdafx.cpp @@ -0,0 +1,15 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.cpp : source file that includes just the standard includes
+// vrad_launcher.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/mp/src/utils/vrad_launcher/stdafx.h b/mp/src/utils/vrad_launcher/stdafx.h new file mode 100644 index 00000000..1c89a814 --- /dev/null +++ b/mp/src/utils/vrad_launcher/stdafx.h @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__4A047C84_94D7_4563_A08C_35E52A52AECC__INCLUDED_)
+#define AFX_STDAFX_H__4A047C84_94D7_4563_A08C_35E52A52AECC__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include <windows.h>
+#include <stdio.h>
+#include "interface.h"
+#include "ivraddll.h"
+
+// TODO: reference additional headers your program requires here
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__4A047C84_94D7_4563_A08C_35E52A52AECC__INCLUDED_)
diff --git a/mp/src/utils/vrad_launcher/vrad_launcher-2010.vcxproj b/mp/src/utils/vrad_launcher/vrad_launcher-2010.vcxproj new file mode 100644 index 00000000..334d4e51 --- /dev/null +++ b/mp/src/utils/vrad_launcher/vrad_launcher-2010.vcxproj @@ -0,0 +1,245 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vrad_launcher</ProjectName>
+ <ProjectGuid>{0B6929D0-4447-E035-E47A-EBFCE557D5B3}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vrad</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vrad</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vrad_launcher.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vrad_launcher;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>Debug/vrad_launcher.pch</PrecompiledHeaderOutputFile>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vrad.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vrad.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vrad_launcher.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vrad_launcher;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>Debug/vrad_launcher.pch</PrecompiledHeaderOutputFile>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vrad.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vrad.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="..\..\public\ivraddll.h" />
+ <ClInclude Include="StdAfx.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="StdAfx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="vrad_launcher.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vrad_launcher/vrad_launcher-2010.vcxproj.filters b/mp/src/utils/vrad_launcher/vrad_launcher-2010.vcxproj.filters new file mode 100644 index 00000000..0790b009 --- /dev/null +++ b/mp/src/utils/vrad_launcher/vrad_launcher-2010.vcxproj.filters @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\ivraddll.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StdAfx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="StdAfx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vrad_launcher.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vrad_launcher/vrad_launcher.cpp b/mp/src/utils/vrad_launcher/vrad_launcher.cpp new file mode 100644 index 00000000..fc458172 --- /dev/null +++ b/mp/src/utils/vrad_launcher/vrad_launcher.cpp @@ -0,0 +1,145 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vrad_launcher.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include <direct.h>
+#include "tier1/strtools.h"
+#include "tier0/icommandline.h"
+
+
+char* GetLastErrorString()
+{
+ static char err[2048];
+
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
+ LocalFree( lpMsgBuf );
+
+ err[ sizeof( err ) - 1 ] = 0;
+
+ return err;
+}
+
+
+void MakeFullPath( const char *pIn, char *pOut, int outLen )
+{
+ if ( pIn[0] == '/' || pIn[0] == '\\' || pIn[1] == ':' )
+ {
+ // It's already a full path.
+ Q_strncpy( pOut, pIn, outLen );
+ }
+ else
+ {
+ _getcwd( pOut, outLen );
+ Q_strncat( pOut, "\\", outLen, COPY_ALL_CHARACTERS );
+ Q_strncat( pOut, pIn, outLen, COPY_ALL_CHARACTERS );
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ char dllName[512];
+
+ CommandLine()->CreateCmdLine( argc, argv );
+
+ // check whether they used the -both switch. If this is specified, vrad will be run
+ // twice, once with -hdr and once without
+ int both_arg=0;
+ for(int arg=1;arg<argc;arg++)
+ if (Q_stricmp(argv[arg],"-both")==0)
+ {
+ both_arg=arg;
+ }
+
+ char fullPath[512], redirectFilename[512];
+ MakeFullPath( argv[0], fullPath, sizeof( fullPath ) );
+ Q_StripFilename( fullPath );
+ Q_snprintf( redirectFilename, sizeof( redirectFilename ), "%s\\%s", fullPath, "vrad.redirect" );
+
+ // First, look for vrad.redirect and load the dll specified in there if possible.
+ CSysModule *pModule = NULL;
+ FILE *fp = fopen( redirectFilename, "rt" );
+ if ( fp )
+ {
+ if ( fgets( dllName, sizeof( dllName ), fp ) )
+ {
+ char *pEnd = strstr( dllName, "\n" );
+ if ( pEnd )
+ *pEnd = 0;
+
+ pModule = Sys_LoadModule( dllName );
+ if ( pModule )
+ printf( "Loaded alternate VRAD DLL (%s) specified in vrad.redirect.\n", dllName );
+ else
+ printf( "Can't find '%s' specified in vrad.redirect.\n", dllName );
+ }
+
+ fclose( fp );
+ }
+
+ int returnValue = 0;
+
+ for(int mode=0;mode<2;mode++)
+ {
+ if (mode && (! both_arg))
+ continue;
+
+
+ // If it didn't load the module above, then use the
+ if ( !pModule )
+ {
+ strcpy( dllName, "vrad_dll.dll" );
+ pModule = Sys_LoadModule( dllName );
+ }
+
+ if( !pModule )
+ {
+ printf( "vrad_launcher error: can't load %s\n%s", dllName, GetLastErrorString() );
+ return 1;
+ }
+
+ CreateInterfaceFn fn = Sys_GetFactory( pModule );
+ if( !fn )
+ {
+ printf( "vrad_launcher error: can't get factory from vrad_dll.dll\n" );
+ Sys_UnloadModule( pModule );
+ return 2;
+ }
+
+ int retCode = 0;
+ IVRadDLL *pDLL = (IVRadDLL*)fn( VRAD_INTERFACE_VERSION, &retCode );
+ if( !pDLL )
+ {
+ printf( "vrad_launcher error: can't get IVRadDLL interface from vrad_dll.dll\n" );
+ Sys_UnloadModule( pModule );
+ return 3;
+ }
+
+ if (both_arg)
+ strcpy(argv[both_arg],(mode)?"-hdr":"-ldr");
+ returnValue = pDLL->main( argc, argv );
+ Sys_UnloadModule( pModule );
+ pModule=0;
+ }
+ return returnValue;
+}
+
diff --git a/mp/src/utils/vtf2tga/vtf2tga-2010.vcxproj b/mp/src/utils/vtf2tga/vtf2tga-2010.vcxproj new file mode 100644 index 00000000..74c38d03 --- /dev/null +++ b/mp/src/utils/vtf2tga/vtf2tga-2010.vcxproj @@ -0,0 +1,261 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vtf2tga</ProjectName>
+ <ProjectGuid>{6B017447-F682-A137-8DF4-4608281F2C9F}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vtf2tga</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vtf2tga</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vtf2tga.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vtf2tga;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vtf2tga.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vtf2tga.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vtf2tga.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vtf2tga;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vtf2tga.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vtf2tga.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib" />
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ <Library Include="..\..\lib\public\vtf.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h" />
+ <ClInclude Include="..\..\public\tier0\basetypes.h" />
+ <ClInclude Include="..\..\public\tier0\commonmacros.h" />
+ <ClInclude Include="..\..\public\tier0\dbg.h" />
+ <ClInclude Include="..\..\public\tier0\fasttimer.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ <ClInclude Include="..\..\public\tier0\memdbgon.h" />
+ <ClInclude Include="..\..\public\tier0\platform.h" />
+ <ClInclude Include="..\..\public\tier0\protected_things.h" />
+ <ClInclude Include="..\..\public\string_t.h" />
+ <ClInclude Include="..\..\public\tier1\strtools.h" />
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\mathlib\vector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector2d.h" />
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h" />
+ <ClInclude Include="..\..\public\vtf\vtf.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="vtf2tga.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vtf2tga/vtf2tga-2010.vcxproj.filters b/mp/src/utils/vtf2tga/vtf2tga-2010.vcxproj.filters new file mode 100644 index 00000000..34553462 --- /dev/null +++ b/mp/src/utils/vtf2tga/vtf2tga-2010.vcxproj.filters @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vtf.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\basetypes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\commonmacros.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\dbg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\fasttimer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\memdbgon.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\platform.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\protected_things.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\string_t.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\strtools.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector2d.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vtf\vtf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vtf2tga.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vtf2tga/vtf2tga.cpp b/mp/src/utils/vtf2tga/vtf2tga.cpp new file mode 100644 index 00000000..08c73340 --- /dev/null +++ b/mp/src/utils/vtf2tga/vtf2tga.cpp @@ -0,0 +1,316 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <direct.h>
+#include "mathlib/mathlib.h"
+#include "bitmap/tgawriter.h"
+#include "tier1/strtools.h"
+#include "vtf/vtf.h"
+#include "tier1/UtlBuffer.h"
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "tier1/utlbuffer.h"
+#include "tier2/tier2.h"
+#include "filesystem.h"
+
+
+//-----------------------------------------------------------------------------
+// HDRFIXME: move this somewhere else.
+//-----------------------------------------------------------------------------
+static void PFMWrite( float *pFloatImage, const char *pFilename, int width, int height )
+{
+ FILE *fp;
+ fp = fopen( pFilename, "wb" );
+ fprintf( fp, "PF\n%d %d\n-1.000000\n", width, height );
+ int i;
+ for( i = height-1; i >= 0; i-- )
+ {
+ float *pRow = &pFloatImage[3 * width * i];
+ fwrite( pRow, width * sizeof( float ) * 3, 1, fp );
+ }
+ fclose( fp );
+}
+
+SpewRetval_t VTF2TGAOutputFunc( SpewType_t spewType, char const *pMsg )
+{
+ printf( pMsg );
+ fflush( stdout );
+
+ if (spewType == SPEW_ERROR)
+ return SPEW_ABORT;
+ return (spewType == SPEW_ASSERT) ? SPEW_DEBUGGER : SPEW_CONTINUE;
+}
+
+static void Usage( void )
+{
+ Error( "Usage: vtf2tga -i <input vtf> [-o <output tga>] [-mip]\n" );
+ exit( -1 );
+}
+
+int main( int argc, char **argv )
+{
+ SpewOutputFunc( VTF2TGAOutputFunc );
+ CommandLine()->CreateCmdLine( argc, argv );
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f, false, false, false, false );
+ InitDefaultFileSystem();
+
+ const char *pVTFFileName = CommandLine()->ParmValue( "-i" );
+ const char *pTGAFileName = CommandLine()->ParmValue( "-o" );
+ bool bGenerateMipLevels = CommandLine()->CheckParm( "-mip" ) != NULL;
+ if ( !pVTFFileName )
+ {
+ Usage();
+ }
+
+ if ( !pTGAFileName )
+ {
+ pTGAFileName = pVTFFileName;
+ }
+
+ char pCurrentDirectory[MAX_PATH];
+ if ( _getcwd( pCurrentDirectory, sizeof(pCurrentDirectory) ) == NULL )
+ {
+ fprintf( stderr, "Unable to get the current directory\n" );
+ return -1;
+ }
+ Q_StripTrailingSlash( pCurrentDirectory );
+
+ char pBuf[MAX_PATH];
+ if ( !Q_IsAbsolutePath( pTGAFileName ) )
+ {
+ Q_snprintf( pBuf, sizeof(pBuf), "%s\\%s", pCurrentDirectory, pTGAFileName );
+ }
+ else
+ {
+ Q_strncpy( pBuf, pTGAFileName, sizeof(pBuf) );
+ }
+ Q_FixSlashes( pBuf );
+
+ char pOutFileNameBase[MAX_PATH];
+ Q_StripExtension( pBuf, pOutFileNameBase, MAX_PATH );
+
+ char pActualVTFFileName[MAX_PATH];
+ Q_strncpy( pActualVTFFileName, pVTFFileName, MAX_PATH );
+ if ( !Q_strstr( pActualVTFFileName, ".vtf" ) )
+ {
+ Q_strcat( pActualVTFFileName, ".vtf", MAX_PATH );
+ }
+
+ FILE *vtfFp = fopen( pActualVTFFileName, "rb" );
+ if( !vtfFp )
+ {
+ Error( "Can't open %s\n", pActualVTFFileName );
+ exit( -1 );
+ }
+
+ fseek( vtfFp, 0, SEEK_END );
+ int srcVTFLength = ftell( vtfFp );
+ fseek( vtfFp, 0, SEEK_SET );
+
+ CUtlBuffer buf;
+ buf.EnsureCapacity( srcVTFLength );
+ int nBytesRead = fread( buf.Base(), 1, srcVTFLength, vtfFp );
+ fclose( vtfFp );
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
+
+ IVTFTexture *pTex = CreateVTFTexture();
+ if (!pTex->Unserialize( buf ))
+ {
+ Error( "*** Error reading in .VTF file %s\n", pActualVTFFileName );
+ exit(-1);
+ }
+
+ Msg( "vtf width: %d\n", pTex->Width() );
+ Msg( "vtf height: %d\n", pTex->Height() );
+ Msg( "vtf numFrames: %d\n", pTex->FrameCount() );
+
+ Msg( "TEXTUREFLAGS_POINTSAMPLE=%s\n", ( pTex->Flags() & TEXTUREFLAGS_POINTSAMPLE ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_TRILINEAR=%s\n", ( pTex->Flags() & TEXTUREFLAGS_TRILINEAR ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_CLAMPS=%s\n", ( pTex->Flags() & TEXTUREFLAGS_CLAMPS ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_CLAMPT=%s\n", ( pTex->Flags() & TEXTUREFLAGS_CLAMPT ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_CLAMPU=%s\n", ( pTex->Flags() & TEXTUREFLAGS_CLAMPU ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_BORDER=%s\n", ( pTex->Flags() & TEXTUREFLAGS_BORDER ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_ANISOTROPIC=%s\n", ( pTex->Flags() & TEXTUREFLAGS_ANISOTROPIC ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_HINT_DXT5=%s\n", ( pTex->Flags() & TEXTUREFLAGS_HINT_DXT5 ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_SRGB=%s\n", ( pTex->Flags() & TEXTUREFLAGS_SRGB ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_NORMAL=%s\n", ( pTex->Flags() & TEXTUREFLAGS_NORMAL ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_NOMIP=%s\n", ( pTex->Flags() & TEXTUREFLAGS_NOMIP ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_NOLOD=%s\n", ( pTex->Flags() & TEXTUREFLAGS_NOLOD ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_ALL_MIPS=%s\n", ( pTex->Flags() & TEXTUREFLAGS_ALL_MIPS ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_PROCEDURAL=%s\n", ( pTex->Flags() & TEXTUREFLAGS_PROCEDURAL ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_ONEBITALPHA=%s\n", ( pTex->Flags() & TEXTUREFLAGS_ONEBITALPHA ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_EIGHTBITALPHA=%s\n", ( pTex->Flags() & TEXTUREFLAGS_EIGHTBITALPHA ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_ENVMAP=%s\n", ( pTex->Flags() & TEXTUREFLAGS_ENVMAP ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_RENDERTARGET=%s\n", ( pTex->Flags() & TEXTUREFLAGS_RENDERTARGET ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_DEPTHRENDERTARGET=%s\n", ( pTex->Flags() & TEXTUREFLAGS_DEPTHRENDERTARGET ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_NODEBUGOVERRIDE=%s\n", ( pTex->Flags() & TEXTUREFLAGS_NODEBUGOVERRIDE ) ? "true" : "false" );
+ Msg( "TEXTUREFLAGS_SINGLECOPY=%s\n", ( pTex->Flags() & TEXTUREFLAGS_SINGLECOPY ) ? "true" : "false" );
+
+ Vector vecReflectivity = pTex->Reflectivity();
+ Msg( "vtf reflectivity: %f %f %f\n", vecReflectivity[0], vecReflectivity[1], vecReflectivity[2] );
+ Msg( "transparency: " );
+ if( pTex->Flags() & TEXTUREFLAGS_EIGHTBITALPHA )
+ {
+ Msg( "eightbitalpha\n" );
+ }
+ else if( pTex->Flags() & TEXTUREFLAGS_ONEBITALPHA )
+ {
+ Msg( "onebitalpha\n" );
+ }
+ else
+ {
+ Msg( "noalpha\n" );
+ }
+ ImageFormat srcFormat = pTex->Format();
+ Msg( "vtf format: %s\n", ImageLoader::GetName( srcFormat ) );
+
+ int iTGANameLen = Q_strlen( pOutFileNameBase );
+
+ int iFaceCount = pTex->FaceCount();
+ int nFrameCount = pTex->FrameCount();
+ bool bIsCubeMap = pTex->IsCubeMap();
+
+ int iLastMipLevel = bGenerateMipLevels ? pTex->MipCount() - 1 : 0;
+ for( int iFrame = 0; iFrame < nFrameCount; ++iFrame )
+ {
+ for ( int iMipLevel = 0; iMipLevel <= iLastMipLevel; ++iMipLevel )
+ {
+ int iWidth, iHeight, iDepth;
+ pTex->ComputeMipLevelDimensions( iMipLevel, &iWidth, &iHeight, &iDepth );
+
+ for (int iCubeFace = 0; iCubeFace < iFaceCount; ++iCubeFace)
+ {
+ for ( int z = 0; z < iDepth; ++z )
+ {
+ // Construct output filename
+ char *pTempNameBuf = (char *)stackalloc( iTGANameLen + 13 );
+ Q_strncpy( pTempNameBuf, pOutFileNameBase, iTGANameLen + 1 );
+ char *pExt = Q_strrchr( pTempNameBuf, '.' );
+ if ( pExt )
+ {
+ pExt = 0;
+ }
+
+ if ( bIsCubeMap )
+ {
+ Assert( pTex->Depth() == 1 ); // shouldn't this be 1 instead of 0?
+ static const char *pCubeFaceName[7] = { "rt", "lf", "bk", "ft", "up", "dn", "sph" };
+ Q_strcat( pTempNameBuf, pCubeFaceName[iCubeFace], iTGANameLen + 13 );
+ }
+
+ if ( nFrameCount > 1 )
+ {
+ char pTemp[4];
+ Q_snprintf( pTemp, 4, "%03d", iFrame );
+ Q_strcat( pTempNameBuf, pTemp, iTGANameLen + 13 );
+ }
+
+ if ( iLastMipLevel != 0 )
+ {
+ char pTemp[8];
+ Q_snprintf( pTemp, 8, "_mip%d", iMipLevel );
+ Q_strcat( pTempNameBuf, pTemp, iTGANameLen + 13 );
+ }
+
+ if ( pTex->Depth() > 1 )
+ {
+ char pTemp[6];
+ Q_snprintf( pTemp, 6, "_z%03d", z );
+ Q_strcat( pTempNameBuf, pTemp, iTGANameLen + 13 );
+ }
+
+ if( srcFormat == IMAGE_FORMAT_RGBA16161616F )
+ {
+ Q_strcat( pTempNameBuf, ".pfm", iTGANameLen + 13 );
+ }
+ else
+ {
+ Q_strcat( pTempNameBuf, ".tga", iTGANameLen + 13 );
+ }
+
+ unsigned char *pSrcImage = pTex->ImageData( iFrame, iCubeFace, iMipLevel, 0, 0, z );
+
+ ImageFormat dstFormat;
+ if( srcFormat == IMAGE_FORMAT_RGBA16161616F )
+ {
+ dstFormat = IMAGE_FORMAT_RGB323232F;
+ }
+ else
+ {
+ if( ImageLoader::IsTransparent( srcFormat ) || (srcFormat == IMAGE_FORMAT_ATI1N ) || (srcFormat == IMAGE_FORMAT_ATI2N ))
+ {
+ dstFormat = IMAGE_FORMAT_BGRA8888;
+ }
+ else
+ {
+ dstFormat = IMAGE_FORMAT_BGR888;
+ }
+ }
+ // dstFormat = IMAGE_FORMAT_RGBA8888;
+ // dstFormat = IMAGE_FORMAT_RGB888;
+ // dstFormat = IMAGE_FORMAT_BGRA8888;
+ // dstFormat = IMAGE_FORMAT_BGR888;
+ // dstFormat = IMAGE_FORMAT_BGRA5551;
+ // dstFormat = IMAGE_FORMAT_BGR565;
+ // dstFormat = IMAGE_FORMAT_BGRA4444;
+ // printf( "dstFormat: %s\n", ImageLoader::GetName( dstFormat ) );
+ unsigned char *pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, dstFormat, false )];
+ if( !ImageLoader::ConvertImageFormat( pSrcImage, srcFormat,
+ pDstImage, dstFormat, iWidth, iHeight, 0, 0 ) )
+ {
+ Error( "Error converting from %s to %s\n",
+ ImageLoader::GetName( srcFormat ), ImageLoader::GetName( dstFormat ) );
+ exit( -1 );
+ }
+
+ if( dstFormat != IMAGE_FORMAT_RGB323232F )
+ {
+ if( ImageLoader::IsTransparent( dstFormat ) && ( dstFormat != IMAGE_FORMAT_RGBA8888 ) )
+ {
+ unsigned char *tmpImage = pDstImage;
+ pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, IMAGE_FORMAT_RGBA8888, false )];
+ if( !ImageLoader::ConvertImageFormat( tmpImage, dstFormat, pDstImage, IMAGE_FORMAT_RGBA8888,
+ iWidth, iHeight, 0, 0 ) )
+ {
+ Error( "Error converting from %s to %s\n",
+ ImageLoader::GetName( dstFormat ), ImageLoader::GetName( IMAGE_FORMAT_RGBA8888 ) );
+ }
+ dstFormat = IMAGE_FORMAT_RGBA8888;
+ }
+ else if( !ImageLoader::IsTransparent( dstFormat ) && ( dstFormat != IMAGE_FORMAT_RGB888 ) )
+ {
+ unsigned char *tmpImage = pDstImage;
+ pDstImage = new unsigned char[ImageLoader::GetMemRequired( iWidth, iHeight, 1, IMAGE_FORMAT_RGB888, false )];
+ if( !ImageLoader::ConvertImageFormat( tmpImage, dstFormat, pDstImage, IMAGE_FORMAT_RGB888,
+ iWidth, iHeight, 0, 0 ) )
+ {
+ Error( "Error converting from %s to %s\n",
+ ImageLoader::GetName( dstFormat ), ImageLoader::GetName( IMAGE_FORMAT_RGB888 ) );
+ }
+ dstFormat = IMAGE_FORMAT_RGB888;
+ }
+
+ CUtlBuffer outBuffer;
+ TGAWriter::WriteToBuffer( pDstImage, outBuffer, iWidth, iHeight,
+ dstFormat, dstFormat );
+ if ( !g_pFullFileSystem->WriteFile( pTempNameBuf, NULL, outBuffer ) )
+ {
+ fprintf( stderr, "unable to write %s\n", pTempNameBuf );
+ }
+ }
+ else
+ {
+ PFMWrite( ( float * )pDstImage, pTempNameBuf, iWidth, iHeight );
+ }
+ }
+ }
+ }
+ }
+
+ // leak leak leak leak leak, leak leak, leak leak (Blue Danube)
+ return 0;
+}
diff --git a/mp/src/utils/vtfdiff/vtfdiff-2010.vcxproj b/mp/src/utils/vtfdiff/vtfdiff-2010.vcxproj new file mode 100644 index 00000000..06d0e26e --- /dev/null +++ b/mp/src/utils/vtfdiff/vtfdiff-2010.vcxproj @@ -0,0 +1,244 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vtfdiff</ProjectName>
+ <ProjectGuid>{81EE9F71-4DFD-8670-B3EA-7B4E931E9845}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vtfdiff</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vtfdiff</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vtfdiff.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vtfdiff;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vtfdiff.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vtfdiff.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vtfdiff.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vtfdiff;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vtfdiff.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vtfdiff.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib" />
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ <Library Include="..\..\lib\public\vtf.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="vtfdiff.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vtfdiff/vtfdiff-2010.vcxproj.filters b/mp/src/utils/vtfdiff/vtfdiff-2010.vcxproj.filters new file mode 100644 index 00000000..59996012 --- /dev/null +++ b/mp/src/utils/vtfdiff/vtfdiff-2010.vcxproj.filters @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\bitmap.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vtf.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vtfdiff.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vtfdiff/vtfdiff.cpp b/mp/src/utils/vtfdiff/vtfdiff.cpp new file mode 100644 index 00000000..bee0d777 --- /dev/null +++ b/mp/src/utils/vtfdiff/vtfdiff.cpp @@ -0,0 +1,438 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "vtf/vtf.h"
+#include "tier1/UtlBuffer.h"
+#include "tier1/utlmap.h"
+#include "bitmap/imageformat.h"
+#include "mathlib/vector.h"
+#include <conio.h>
+
+void Usage( void )
+{
+ printf( "Usage: vtfdiff file1.vtf file2.vtf\n" );
+}
+
+bool LoadFileIntoBuffer( const char *pFileName, CUtlBuffer &buf )
+{
+ struct _stat statBuf;
+ if( _stat( pFileName, &statBuf ) != 0 )
+ {
+ goto error;
+ }
+
+ buf.EnsureCapacity( statBuf.st_size );
+ FILE *fp;
+ fp = fopen( pFileName, "rb" );
+ if( !fp )
+ {
+ goto error;
+ }
+
+ int nBytesRead = fread( buf.Base(), 1, statBuf.st_size, fp );
+ fclose( fp );
+
+ buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
+ return true;
+
+error:
+ printf( "Can't find file %s\n", pFileName );
+ return false;
+}
+
+char const * ResourceToString( uint32 uiResType )
+{
+ static char chBuffer[256];
+
+ switch ( uiResType )
+ {
+ case VTF_LEGACY_RSRC_LOW_RES_IMAGE:
+ return "VTF_LEGACY_RSRC_LOW_RES_IMAGE";
+ case VTF_LEGACY_RSRC_IMAGE:
+ return "VTF_LEGACY_RSRC_IMAGE";
+ case VTF_RSRC_SHEET:
+ return "VTF_RSRC_SHEET";
+ case MK_VTF_RSRC_ID( 'C','R','C' ):
+ return "CRC";
+ case VTF_RSRC_TEXTURE_LOD_SETTINGS:
+ return "VTF_RSRC_TEXTURE_LOD_SETTINGS";
+
+ default:
+ sprintf( chBuffer, "0x%08X", uiResType );
+ return chBuffer;
+ }
+
+ return chBuffer;
+}
+
+void PrintFlags( int flags )
+{
+#define PRNFLAG( flagname ) \
+ if ( ( flags & (flagname) ) == (flagname) ) \
+ { \
+ flags &=~ (flagname); \
+ printf( "%s%s", #flagname + strlen("TEXTUREFLAGS_"), flags ? "|" : "" ); \
+ } \
+
+
+ PRNFLAG( TEXTUREFLAGS_POINTSAMPLE )
+ PRNFLAG( TEXTUREFLAGS_TRILINEAR )
+ PRNFLAG( TEXTUREFLAGS_CLAMPS )
+ PRNFLAG( TEXTUREFLAGS_CLAMPT )
+ PRNFLAG( TEXTUREFLAGS_ANISOTROPIC )
+ PRNFLAG( TEXTUREFLAGS_HINT_DXT5 )
+ PRNFLAG( TEXTUREFLAGS_SRGB )
+ PRNFLAG( TEXTUREFLAGS_NORMAL )
+ PRNFLAG( TEXTUREFLAGS_NOMIP )
+ PRNFLAG( TEXTUREFLAGS_NOLOD )
+ PRNFLAG( TEXTUREFLAGS_ALL_MIPS )
+ PRNFLAG( TEXTUREFLAGS_PROCEDURAL )
+ PRNFLAG( TEXTUREFLAGS_ONEBITALPHA )
+ PRNFLAG( TEXTUREFLAGS_EIGHTBITALPHA )
+ PRNFLAG( TEXTUREFLAGS_ENVMAP )
+ PRNFLAG( TEXTUREFLAGS_RENDERTARGET )
+ PRNFLAG( TEXTUREFLAGS_DEPTHRENDERTARGET )
+ PRNFLAG( TEXTUREFLAGS_NODEBUGOVERRIDE )
+ PRNFLAG( TEXTUREFLAGS_SINGLECOPY )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_00080000 )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_00100000 )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_00200000 )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_00400000 )
+ PRNFLAG( TEXTUREFLAGS_NODEPTHBUFFER )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_01000000 )
+ PRNFLAG( TEXTUREFLAGS_CLAMPU )
+ PRNFLAG( TEXTUREFLAGS_VERTEXTEXTURE )
+ PRNFLAG( TEXTUREFLAGS_SSBUMP )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_10000000 )
+ PRNFLAG( TEXTUREFLAGS_BORDER )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_40000000 )
+ PRNFLAG( TEXTUREFLAGS_UNUSED_80000000 )
+
+#undef PRNFLAG
+
+ if ( flags )
+ {
+ printf( "0x%08X", flags );
+ }
+}
+
+int main( int argc, char **argv )
+{
+ if( argc != 3 )
+ {
+ Usage();
+ return 10;
+ }
+
+ CUtlBuffer file1;
+ CUtlBuffer file2;
+
+ if ( !LoadFileIntoBuffer( argv[1], file1 ) )
+ return 21;
+ if ( !LoadFileIntoBuffer( argv[2], file2 ) )
+ return 22;
+
+ IVTFTexture *pTexture1 = CreateVTFTexture();
+ IVTFTexture *pTexture2 = CreateVTFTexture();
+
+ IVTFTexture *arrTextures[2] = { pTexture1, pTexture2 };
+
+ bool bMatch = true;
+
+ if( !pTexture1->Unserialize( file1 ) )
+ {
+ printf( "error loading %s\n", argv[1] );
+ return 31;
+ }
+ if( !pTexture2->Unserialize( file2 ) )
+ {
+ printf( "error loading %s\n", argv[2] );
+ return 32;
+ }
+
+ if( pTexture1->Width() != pTexture2->Width() ||
+ pTexture1->Height() != pTexture2->Height() ||
+ pTexture1->Depth() != pTexture2->Depth() )
+ {
+ printf( "%s dimensions differ: %dx%dx%d != %dx%dx%d\n",
+ argv[1],
+ ( int )pTexture1->Width(), ( int )pTexture1->Height(), ( int )pTexture1->Depth(),
+ ( int )pTexture2->Width(), ( int )pTexture2->Height(), ( int )pTexture2->Depth() );
+ bMatch = false;
+ }
+
+ if( pTexture1->LowResWidth() != pTexture2->LowResWidth() ||
+ pTexture1->LowResHeight() != pTexture2->LowResHeight() )
+ {
+ printf( "%s lowres dimensions differ: %dx%d != %dx%d\n",
+ argv[1],
+ ( int )pTexture1->LowResWidth(), ( int )pTexture1->LowResHeight(),
+ ( int )pTexture2->LowResWidth(), ( int )pTexture2->LowResHeight() );
+ bMatch = false;
+ }
+
+ if( pTexture1->MipCount() != pTexture2->MipCount() )
+ {
+ printf( "%s differing mipcounts: %d != %d\n",
+ argv[1],
+ ( int )pTexture1->MipCount(), ( int )pTexture2->MipCount() );
+ bMatch = false;
+ }
+
+ if( pTexture1->FaceCount() != pTexture2->FaceCount() )
+ {
+ printf( "%s differing facecount: %d != %d\n",
+ argv[1],
+ ( int )pTexture1->FaceCount(), ( int )pTexture2->FaceCount() );
+ bMatch = false;
+ }
+
+ if( pTexture1->FrameCount() != pTexture2->FrameCount() )
+ {
+ printf( "%s differing framecount: %d != %d\n",
+ argv[1],
+ ( int )pTexture1->FrameCount(), ( int )pTexture2->FrameCount() );
+ bMatch = false;
+ }
+
+ if( pTexture1->Flags() != pTexture2->Flags() )
+ {
+ printf( "%s differing flags: \"",
+ argv[1] );
+ PrintFlags( pTexture1->Flags() );
+ printf( "\" != \"" );
+ PrintFlags( pTexture2->Flags() );
+ printf( "\"\n" );
+ bMatch = false;
+ }
+
+ if( pTexture1->BumpScale() != pTexture2->BumpScale() )
+ {
+ printf( "%s differing bumpscale: %f != %f\n",
+ argv[1],
+ ( float )pTexture1->BumpScale(), ( float )pTexture2->BumpScale() );
+ bMatch = false;
+ }
+
+ if( pTexture1->Format() != pTexture2->Format() )
+ {
+ printf( "%s differing image format: %s != %s\n",
+ argv[1],
+ ImageLoader::GetName( pTexture1->Format() ),
+ ImageLoader::GetName( pTexture2->Format() ) );
+ bMatch = false;
+ }
+
+ if( pTexture1->LowResFormat() != pTexture2->LowResFormat() )
+ {
+ Assert(0);
+ printf( "%s differing lowres image format: %s != %s\n",
+ argv[1],
+ ImageLoader::GetName( pTexture1->LowResFormat() ),
+ ImageLoader::GetName( pTexture2->LowResFormat() ) );
+ bMatch = false;
+ }
+
+ const Vector &vReflectivity1 = pTexture1->Reflectivity();
+ const Vector &vReflectivity2 = pTexture2->Reflectivity();
+ if( !VectorsAreEqual( vReflectivity1, vReflectivity2, 0.0001f ) )
+ {
+ printf( "%s differing reflectivity: [%f,%f,%f] != [%f,%f,%f]\n",
+ argv[1],
+ ( float )pTexture1->Reflectivity()[0],
+ ( float )pTexture1->Reflectivity()[1],
+ ( float )pTexture1->Reflectivity()[2],
+ ( float )pTexture2->Reflectivity()[0],
+ ( float )pTexture2->Reflectivity()[1],
+ ( float )pTexture2->Reflectivity()[2] );
+ bMatch = false;
+ }
+
+ if ( pTexture1->ComputeTotalSize() != pTexture2->ComputeTotalSize() )
+ {
+ printf( "%s differing image data size: %d != %d\n",
+ argv[1],
+ ( int )pTexture1->ComputeTotalSize(), ( int )pTexture2->ComputeTotalSize() );
+ bMatch = false;
+ }
+
+ if ( bMatch )
+ {
+ unsigned char const *pData1 = pTexture1->ImageData();
+ unsigned char const *pData2 = pTexture2->ImageData();
+
+ int const iSize = pTexture1->ComputeTotalSize();
+
+ if( memcmp( pData1, pData2, iSize) != 0 )
+ {
+ printf( "%s image data different\n", argv[1] );
+
+ if (( pTexture1->Format() == IMAGE_FORMAT_DXT1 ) || ( pTexture1->Format() == IMAGE_FORMAT_DXT3 ) ||
+ ( pTexture1->Format() == IMAGE_FORMAT_DXT5 ) || ( pTexture1->Format() == IMAGE_FORMAT_ATI2N ) ||
+ ( pTexture1->Format() == IMAGE_FORMAT_ATI1N ) )
+ {
+ int i, numOffsetsComplained = 0;
+ for( i = 0; i < iSize; i++ )
+ {
+ if( pData1[i] != pData2[i] )
+ {
+ printf( "image data at offset %d different\n", i );
+ if ( numOffsetsComplained ++ > 10 )
+ {
+ printf( "image data significantly differs!\n" );
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for( int iFrame = 0; iFrame < pTexture1->FrameCount(); ++iFrame )
+ {
+ for ( int iMipLevel = 0; iMipLevel < pTexture1->MipCount(); ++iMipLevel )
+ {
+ int nMipWidth, nMipHeight, nMipDepth;
+ pTexture1->ComputeMipLevelDimensions( iMipLevel, &nMipWidth, &nMipHeight, &nMipDepth );
+
+ for (int iCubeFace = 0; iCubeFace < pTexture1->FrameCount(); ++iCubeFace)
+ {
+ for ( int z = 0; z < nMipDepth; ++z )
+ {
+ pData1 = pTexture1->ImageData( iFrame, iCubeFace, iMipLevel, 0, 0, z );
+ pData2 = pTexture2->ImageData( iFrame, iCubeFace, iMipLevel, 0, 0, z );
+
+ int nMipSize = pTexture1->ComputeMipSize( iMipLevel );
+ if ( memcmp( pData1, pData2, nMipSize ) )
+ {
+ bool bBreak = false;
+
+ for ( int y = 0; y < nMipHeight; ++y )
+ {
+ for ( int x = 0; x < nMipWidth; ++x )
+ {
+ unsigned char const *pData1a = pTexture1->ImageData( iFrame, iCubeFace, iMipLevel, x, y, z );
+ unsigned char const *pData2a = pTexture2->ImageData( iFrame, iCubeFace, iMipLevel, x, y, z );
+
+ if ( memcmp( pData1a, pData2a, ImageLoader::SizeInBytes( pTexture1->Format() ) ) )
+ {
+ printf( "Frame %d Mip level %d Face %d Z-slice %d texel (%d,%d) different!\n",
+ iFrame, iMipLevel, iCubeFace, z, x, y );
+ bBreak = true;
+ break;
+ }
+ }
+
+ if ( bBreak )
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ bMatch = false;
+ }
+ }
+
+ // Lowres data
+ {
+ int iDummy, iSize1, iSize2;
+ pTexture1->LowResFileInfo( &iDummy, &iSize1 );
+ pTexture2->LowResFileInfo( &iDummy, &iSize2 );
+
+ if ( iSize1 != iSize2 )
+ {
+ printf( "%s differing low res image data size: %d != %d\n", argv[1], iSize1, iSize2 );
+ bMatch = false;
+ }
+
+ if ( bMatch )
+ {
+ if ( memcmp( pTexture1->LowResImageData(), pTexture2->LowResImageData(), iSize1 ) != 0 )
+ {
+ printf( "%s differing low res image data\n",
+ argv[1] );
+ bMatch = false;
+ }
+ }
+ }
+
+ // Check other resources
+ {
+ int numRes1 = pTexture1->GetResourceTypes( NULL, 0 );
+ int numRes2 = pTexture2->GetResourceTypes( NULL, 0 );
+
+ // List of resource types checked or insignificant diffs
+ typedef CUtlMap< int, bool > MapResTypes;
+ MapResTypes mapTypes( DefLessFunc( int ) );
+ mapTypes.Insert( VTF_LEGACY_RSRC_LOW_RES_IMAGE, true );
+ mapTypes.Insert( VTF_LEGACY_RSRC_IMAGE, true );
+ mapTypes.Insert( MK_VTF_RSRC_ID( 'C','R','C' ), true );
+
+ uint32 *puiresbuffer = ( uint32 * ) stackalloc( ( numRes1 + numRes2 ) * sizeof( uint32 ) );
+
+ int arrNums[2] = { numRes1, numRes2 };
+
+ for ( int itx = 0; itx < 2; ++ itx )
+ {
+ arrTextures[itx]->GetResourceTypes( puiresbuffer, arrNums[itx] );
+ while ( arrNums[itx] --> 0 )
+ {
+ uint32 uiResType = puiresbuffer[ arrNums[itx] ];
+ if ( mapTypes.Find( uiResType ) != mapTypes.InvalidIndex() )
+ continue;
+
+ mapTypes.Insert( uiResType, true );
+
+ size_t numBytes1, numBytes2;
+ void const *pvResData1 = pTexture1->GetResourceData( uiResType, &numBytes1 );
+ void const *pvResData2 = pTexture2->GetResourceData( uiResType, &numBytes2 );
+
+ if ( !pvResData1 != !pvResData2 )
+ {
+ printf( "%s different resource %s %s\n",
+ argv[1],
+ ResourceToString( uiResType ),
+ pvResData1 ? "present" : "missing" );
+ bMatch = false;
+ }
+ else if ( numBytes1 != numBytes2 )
+ {
+ printf( "%s different resource %s size %lld != %lld\n",
+ argv[1],
+ ResourceToString( uiResType ),
+ (long long)numBytes1, (long long)numBytes2 );
+ bMatch = false;
+ }
+ else if ( memcmp( pvResData1, pvResData2, numBytes1 ) != 0 )
+ {
+ printf( "%s different resource %s data\n",
+ argv[1],
+ ResourceToString( uiResType ) );
+ bMatch = false;
+ }
+ }
+ }
+ }
+
+
+
+ if( bMatch )
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
diff --git a/mp/src/utils/vvis/WaterDist.cpp b/mp/src/utils/vvis/WaterDist.cpp new file mode 100644 index 00000000..5ed380c8 --- /dev/null +++ b/mp/src/utils/vvis/WaterDist.cpp @@ -0,0 +1,30 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "bsplib.h"
+
+// input:
+// from bsplib.h:
+// numleafs
+// dleafs
+
+void EmitDistanceToWaterInfo( void )
+{
+ int leafID;
+ for( leafID = 0; leafID < numleafs; leafID++ )
+ {
+ dleaf_t *pLeaf = &dleafs[leafID];
+ if( pLeaf->leafWaterDataID == -1 )
+ {
+ // FIXME: set the distance to water to infinity here just in case.
+ continue;
+ }
+
+ // Get the vis set for this leaf.
+
+ }
+}
+
diff --git a/mp/src/utils/vvis/flow.cpp b/mp/src/utils/vvis/flow.cpp new file mode 100644 index 00000000..7234e68a --- /dev/null +++ b/mp/src/utils/vvis/flow.cpp @@ -0,0 +1,881 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "vis.h"
+#include "vmpi.h"
+
+int g_TraceClusterStart = -1;
+int g_TraceClusterStop = -1;
+/*
+
+ each portal will have a list of all possible to see from first portal
+
+ if (!thread->portalmightsee[portalnum])
+
+ portal mightsee
+
+ for p2 = all other portals in leaf
+ get sperating planes
+ for all portals that might be seen by p2
+ mark as unseen if not present in seperating plane
+ flood fill a new mightsee
+ save as passagemightsee
+
+
+ void CalcMightSee (leaf_t *leaf,
+*/
+
+int CountBits (byte *bits, int numbits)
+{
+ int i;
+ int c;
+
+ c = 0;
+ for (i=0 ; i<numbits ; i++)
+ if ( CheckBit( bits, i ) )
+ c++;
+
+ return c;
+}
+
+int c_fullskip;
+int c_portalskip, c_leafskip;
+int c_vistest, c_mighttest;
+
+int c_chop, c_nochop;
+
+int active;
+
+extern bool g_bVMPIEarlyExit;
+
+
+void CheckStack (leaf_t *leaf, threaddata_t *thread)
+{
+ pstack_t *p, *p2;
+
+ for (p=thread->pstack_head.next ; p ; p=p->next)
+ {
+// Msg ("=");
+ if (p->leaf == leaf)
+ Error ("CheckStack: leaf recursion");
+ for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
+ if (p2->leaf == p->leaf)
+ Error ("CheckStack: late leaf recursion");
+ }
+// Msg ("\n");
+}
+
+
+winding_t *AllocStackWinding (pstack_t *stack)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (stack->freewindings[i])
+ {
+ stack->freewindings[i] = 0;
+ return &stack->windings[i];
+ }
+ }
+
+ Error ("Out of memory. AllocStackWinding: failed");
+
+ return NULL;
+}
+
+void FreeStackWinding (winding_t *w, pstack_t *stack)
+{
+ int i;
+
+ i = w - stack->windings;
+
+ if (i<0 || i>2)
+ return; // not from local
+
+ if (stack->freewindings[i])
+ Error ("FreeStackWinding: allready free");
+ stack->freewindings[i] = 1;
+}
+
+/*
+==============
+ChopWinding
+
+==============
+*/
+
+#ifdef _WIN32
+#pragma warning (disable:4701)
+#endif
+
+winding_t *ChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
+{
+ vec_t dists[128];
+ int sides[128];
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ Vector mid;
+ winding_t *neww;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dot = DotProduct (in->points[i], split->normal);
+ dot -= split->dist;
+ dists[i] = dot;
+ if (dot > ON_VIS_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_VIS_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+
+ if (!counts[1])
+ return in; // completely on front side
+
+ if (!counts[0])
+ {
+ FreeStackWinding (in, stack);
+ return NULL;
+ }
+
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ neww = AllocStackWinding (stack);
+
+ neww->numpoints = 0;
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ Vector& p1 = in->points[i];
+
+ if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
+ {
+ FreeStackWinding (neww, stack);
+ return in; // can't chop -- fall back to original
+ }
+
+ if (sides[i] == SIDE_ON)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ continue;
+ }
+
+ if (sides[i] == SIDE_FRONT)
+ {
+ VectorCopy (p1, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
+ {
+ FreeStackWinding (neww, stack);
+ return in; // can't chop -- fall back to original
+ }
+
+ // generate a split point
+ Vector& p2 = in->points[(i+1)%in->numpoints];
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ { // avoid round off error when possible
+ if (split->normal[j] == 1)
+ mid[j] = split->dist;
+ else if (split->normal[j] == -1)
+ mid[j] = -split->dist;
+ else
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, neww->points[neww->numpoints]);
+ neww->numpoints++;
+ }
+
+// free the original winding
+ FreeStackWinding (in, stack);
+
+ return neww;
+}
+
+#ifdef _WIN32
+#pragma warning (default:4701)
+#endif
+
+/*
+==============
+ClipToSeperators
+
+Source, pass, and target are an ordering of portals.
+
+Generates seperating planes canidates by taking two points from source and one
+point from pass, and clips target by them.
+
+If target is totally clipped away, that portal can not be seen through.
+
+Normal clip keeps target on the same side as pass, which is correct if the
+order goes source, pass, target. If the order goes pass, source, target then
+flipclip should be set.
+==============
+*/
+winding_t *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, bool flipclip, pstack_t *stack)
+{
+ int i, j, k, l;
+ plane_t plane;
+ Vector v1, v2;
+ float d;
+ vec_t length;
+ int counts[3];
+ bool fliptest;
+
+// check all combinations
+ for (i=0 ; i<source->numpoints ; i++)
+ {
+ l = (i+1)%source->numpoints;
+ VectorSubtract (source->points[l] , source->points[i], v1);
+
+ // fing a vertex of pass that makes a plane that puts all of the
+ // vertexes of pass on the front side and all of the vertexes of
+ // source on the back side
+ for (j=0 ; j<pass->numpoints ; j++)
+ {
+ VectorSubtract (pass->points[j], source->points[i], v2);
+
+ plane.normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
+ plane.normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
+ plane.normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
+
+ // if points don't make a valid plane, skip it
+
+ length = plane.normal[0] * plane.normal[0]
+ + plane.normal[1] * plane.normal[1]
+ + plane.normal[2] * plane.normal[2];
+
+ if (length < ON_VIS_EPSILON)
+ continue;
+
+ length = 1/sqrt(length);
+
+ plane.normal[0] *= length;
+ plane.normal[1] *= length;
+ plane.normal[2] *= length;
+
+ plane.dist = DotProduct (pass->points[j], plane.normal);
+
+ //
+ // find out which side of the generated seperating plane has the
+ // source portal
+ //
+#if 1
+ fliptest = false;
+ for (k=0 ; k<source->numpoints ; k++)
+ {
+ if (k == i || k == l)
+ continue;
+ d = DotProduct (source->points[k], plane.normal) - plane.dist;
+ if (d < -ON_VIS_EPSILON)
+ { // source is on the negative side, so we want all
+ // pass and target on the positive side
+ fliptest = false;
+ break;
+ }
+ else if (d > ON_VIS_EPSILON)
+ { // source is on the positive side, so we want all
+ // pass and target on the negative side
+ fliptest = true;
+ break;
+ }
+ }
+ if (k == source->numpoints)
+ continue; // planar with source portal
+#else
+ fliptest = flipclip;
+#endif
+ //
+ // flip the normal if the source portal is backwards
+ //
+ if (fliptest)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+#if 1
+ //
+ // if all of the pass portal points are now on the positive side,
+ // this is the seperating plane
+ //
+ counts[0] = counts[1] = counts[2] = 0;
+ for (k=0 ; k<pass->numpoints ; k++)
+ {
+ if (k==j)
+ continue;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_VIS_EPSILON)
+ break;
+ else if (d > ON_VIS_EPSILON)
+ counts[0]++;
+ else
+ counts[2]++;
+ }
+ if (k != pass->numpoints)
+ continue; // points on negative side, not a seperating plane
+
+ if (!counts[0])
+ continue; // planar with seperating plane
+#else
+ k = (j+1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_VIS_EPSILON)
+ continue;
+ k = (j+pass->numpoints-1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_VIS_EPSILON)
+ continue;
+#endif
+ //
+ // flip the normal if we want the back side
+ //
+ if (flipclip)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+
+ //
+ // clip target by the seperating plane
+ //
+ target = ChopWinding (target, stack, &plane);
+ if (!target)
+ return NULL; // target is not visible
+
+ // JAY: End the loop, no need to find additional separators on this edge ?
+// j = pass->numpoints;
+ }
+ }
+
+ return target;
+}
+
+
+class CPortalTrace
+{
+public:
+ CUtlVector<Vector> m_list;
+ CThreadFastMutex m_mutex;
+} g_PortalTrace;
+
+void WindingCenter (winding_t *w, Vector ¢er)
+{
+ int i;
+ float scale;
+
+ VectorCopy (vec3_origin, center);
+ for (i=0 ; i<w->numpoints ; i++)
+ VectorAdd (w->points[i], center, center);
+
+ scale = 1.0/w->numpoints;
+ VectorScale (center, scale, center);
+}
+
+Vector ClusterCenter( int cluster )
+{
+ Vector mins, maxs;
+ ClearBounds(mins, maxs);
+ int count = leafs[cluster].portals.Count();
+ for ( int i = 0; i < count; i++ )
+ {
+ winding_t *w = leafs[cluster].portals[i]->winding;
+ for ( int j = 0; j < w->numpoints; j++ )
+ {
+ AddPointToBounds( w->points[j], mins, maxs );
+ }
+ }
+ return (mins + maxs) * 0.5f;
+}
+
+
+void DumpPortalTrace( pstack_t *pStack )
+{
+ AUTO_LOCK_FM(g_PortalTrace.m_mutex);
+ if ( g_PortalTrace.m_list.Count() )
+ return;
+
+ Warning("Dumped cluster trace!!!\n");
+ Vector mid;
+ mid = ClusterCenter( g_TraceClusterStart );
+ g_PortalTrace.m_list.AddToTail(mid);
+ for ( ; pStack != NULL; pStack = pStack->next )
+ {
+ winding_t *w = pStack->pass ? pStack->pass : pStack->portal->winding;
+ WindingCenter (w, mid);
+ g_PortalTrace.m_list.AddToTail(mid);
+ for ( int i = 0; i < w->numpoints; i++ )
+ {
+ g_PortalTrace.m_list.AddToTail(w->points[i]);
+ g_PortalTrace.m_list.AddToTail(mid);
+ }
+ for ( int i = 0; i < w->numpoints; i++ )
+ {
+ g_PortalTrace.m_list.AddToTail(w->points[i]);
+ }
+ g_PortalTrace.m_list.AddToTail(w->points[0]);
+ g_PortalTrace.m_list.AddToTail(mid);
+ }
+ mid = ClusterCenter( g_TraceClusterStop );
+ g_PortalTrace.m_list.AddToTail(mid);
+}
+
+void WritePortalTrace( const char *source )
+{
+ Vector mid;
+ FILE *linefile;
+ char filename[1024];
+
+ if ( !g_PortalTrace.m_list.Count() )
+ {
+ Warning("No trace generated from %d to %d\n", g_TraceClusterStart, g_TraceClusterStop );
+ return;
+ }
+
+ sprintf (filename, "%s.lin", source);
+ linefile = fopen (filename, "w");
+ if (!linefile)
+ Error ("Couldn't open %s\n", filename);
+
+ for ( int i = 0; i < g_PortalTrace.m_list.Count(); i++ )
+ {
+ Vector p = g_PortalTrace.m_list[i];
+ fprintf (linefile, "%f %f %f\n", p[0], p[1], p[2]);
+ }
+ fclose (linefile);
+ Warning("Wrote %s!!!\n", filename);
+}
+
+/*
+==================
+RecursiveLeafFlow
+
+Flood fill through the leafs
+If src_portal is NULL, this is the originating leaf
+==================
+*/
+void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
+{
+ pstack_t stack;
+ portal_t *p;
+ plane_t backplane;
+ leaf_t *leaf;
+ int i, j;
+ long *test, *might, *vis, more;
+ int pnum;
+
+ // Early-out if we're a VMPI worker that's told to exit. If we don't do this here, then the
+ // worker might spin its wheels for a while on an expensive work unit and not be available to the pool.
+ // This is pretty common in vis.
+ if ( g_bVMPIEarlyExit )
+ return;
+
+ if ( leafnum == g_TraceClusterStop )
+ {
+ DumpPortalTrace(&thread->pstack_head);
+ return;
+ }
+ thread->c_chains++;
+
+ leaf = &leafs[leafnum];
+
+ prevstack->next = &stack;
+
+ stack.next = NULL;
+ stack.leaf = leaf;
+ stack.portal = NULL;
+
+ might = (long *)stack.mightsee;
+ vis = (long *)thread->base->portalvis;
+
+ // check all portals for flowing into other leafs
+ for (i=0 ; i<leaf->portals.Count() ; i++)
+ {
+
+ p = leaf->portals[i];
+ pnum = p - portals;
+
+ if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
+ {
+ continue; // can't possibly see it
+ }
+
+ // if the portal can't see anything we haven't allready seen, skip it
+ if (p->status == stat_done)
+ {
+ test = (long *)p->portalvis;
+ }
+ else
+ {
+ test = (long *)p->portalflood;
+ }
+
+ more = 0;
+ for (j=0 ; j<portallongs ; j++)
+ {
+ might[j] = ((long *)prevstack->mightsee)[j] & test[j];
+ more |= (might[j] & ~vis[j]);
+ }
+
+ if ( !more && CheckBit( thread->base->portalvis, pnum ) )
+ { // can't see anything new
+ continue;
+ }
+
+ // get plane of portal, point normal into the neighbor leaf
+ stack.portalplane = p->plane;
+ VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
+ backplane.dist = -p->plane.dist;
+
+ stack.portal = p;
+ stack.next = NULL;
+ stack.freewindings[0] = 1;
+ stack.freewindings[1] = 1;
+ stack.freewindings[2] = 1;
+
+ float d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
+ d -= thread->pstack_head.portalplane.dist;
+ if (d < -p->radius)
+ {
+ continue;
+ }
+ else if (d > p->radius)
+ {
+ stack.pass = p->winding;
+ }
+ else
+ {
+ stack.pass = ChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+ if (!stack.pass)
+ continue;
+ }
+
+
+ d = DotProduct (thread->base->origin, p->plane.normal);
+ d -= p->plane.dist;
+ if (d > thread->base->radius)
+ {
+ continue;
+ }
+ else if (d < -thread->base->radius)
+ {
+ stack.source = prevstack->source;
+ }
+ else
+ {
+ stack.source = ChopWinding (prevstack->source, &stack, &backplane);
+ if (!stack.source)
+ continue;
+ }
+
+
+ if (!prevstack->pass)
+ { // the second leaf can only be blocked if coplanar
+
+ // mark the portal as visible
+ SetBit( thread->base->portalvis, pnum );
+
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+ continue;
+ }
+
+ stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, false, &stack);
+ if (!stack.pass)
+ continue;
+
+ stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, true, &stack);
+ if (!stack.pass)
+ continue;
+
+ // mark the portal as visible
+ SetBit( thread->base->portalvis, pnum );
+
+ // flow through it for real
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+ }
+}
+
+
+/*
+===============
+PortalFlow
+
+generates the portalvis bit vector
+===============
+*/
+void PortalFlow (int iThread, int portalnum)
+{
+ threaddata_t data;
+ int i;
+ portal_t *p;
+ int c_might, c_can;
+
+ p = sorted_portals[portalnum];
+ p->status = stat_working;
+
+ c_might = CountBits (p->portalflood, g_numportals*2);
+
+ memset (&data, 0, sizeof(data));
+ data.base = p;
+
+ data.pstack_head.portal = p;
+ data.pstack_head.source = p->winding;
+ data.pstack_head.portalplane = p->plane;
+ for (i=0 ; i<portallongs ; i++)
+ ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
+
+ RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
+
+
+ p->status = stat_done;
+
+ c_can = CountBits (p->portalvis, g_numportals*2);
+
+ qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
+ (int)(p - portals), c_might, c_can, data.c_chains);
+}
+
+
+/*
+===============================================================================
+
+This is a rough first-order aproximation that is used to trivially reject some
+of the final calculations.
+
+
+Calculates portalfront and portalflood bit vectors
+
+===============================================================================
+*/
+
+int c_flood, c_vis;
+
+/*
+==================
+SimpleFlood
+
+==================
+*/
+void SimpleFlood (portal_t *srcportal, int leafnum)
+{
+ int i;
+ leaf_t *leaf;
+ portal_t *p;
+ int pnum;
+
+ leaf = &leafs[leafnum];
+
+ for (i=0 ; i<leaf->portals.Count(); i++)
+ {
+ p = leaf->portals[i];
+ pnum = p - portals;
+ if ( !CheckBit( srcportal->portalfront, pnum ) )
+ continue;
+
+ if ( CheckBit( srcportal->portalflood, pnum ) )
+ continue;
+
+ SetBit( srcportal->portalflood, pnum );
+
+ SimpleFlood (srcportal, p->leaf);
+ }
+}
+
+/*
+==============
+BasePortalVis
+==============
+*/
+void BasePortalVis (int iThread, int portalnum)
+{
+ int j, k;
+ portal_t *tp, *p;
+ float d;
+ winding_t *w;
+ Vector segment;
+ double dist2, minDist2;
+
+ // get the portal
+ p = portals+portalnum;
+
+ //
+ // allocate memory for bitwise vis solutions for this portal
+ //
+ p->portalfront = (byte*)malloc (portalbytes);
+ memset (p->portalfront, 0, portalbytes);
+
+ p->portalflood = (byte*)malloc (portalbytes);
+ memset (p->portalflood, 0, portalbytes);
+
+ p->portalvis = (byte*)malloc (portalbytes);
+ memset (p->portalvis, 0, portalbytes);
+
+ //
+ // test the given portal against all of the portals in the map
+ //
+ for (j=0, tp = portals ; j<g_numportals*2 ; j++, tp++)
+ {
+ // don't test against itself
+ if (j == portalnum)
+ continue;
+
+ //
+ //
+ //
+ w = tp->winding;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ d = DotProduct (w->points[k], p->plane.normal) - p->plane.dist;
+ if (d > ON_VIS_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ //
+ //
+ //
+ w = p->winding;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ d = DotProduct (w->points[k], tp->plane.normal) - tp->plane.dist;
+ if (d < -ON_VIS_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ //
+ // if using radius visibility -- check to see if any portal points lie inside of the
+ // radius given
+ //
+ if( g_bUseRadius )
+ {
+ w = tp->winding;
+ minDist2 = 1024000000.0; // 32000^2
+ for( k = 0; k < w->numpoints; k++ )
+ {
+ VectorSubtract( w->points[k], p->origin, segment );
+ dist2 = ( segment[0] * segment[0] ) + ( segment[1] * segment[1] ) + ( segment[2] * segment[2] );
+ if( dist2 < minDist2 )
+ {
+ minDist2 = dist2;
+ }
+ }
+
+ if( minDist2 > g_VisRadius )
+ continue;
+ }
+
+ // add current portal to given portal's list of visible portals
+ SetBit( p->portalfront, j );
+ }
+
+ SimpleFlood (p, p->leaf);
+
+ p->nummightsee = CountBits (p->portalflood, g_numportals*2);
+// Msg ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
+ c_flood += p->nummightsee;
+}
+
+
+
+
+
+/*
+===============================================================================
+
+This is a second order aproximation
+
+Calculates portalvis bit vector
+
+WAAAAAAY too slow.
+
+===============================================================================
+*/
+
+/*
+==================
+RecursiveLeafBitFlow
+
+==================
+*/
+void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
+{
+ portal_t *p;
+ leaf_t *leaf;
+ int i, j;
+ long more;
+ int pnum;
+ byte newmight[MAX_PORTALS/8];
+
+ leaf = &leafs[leafnum];
+
+// check all portals for flowing into other leafs
+ for (i=0 ; i<leaf->portals.Count(); i++)
+ {
+ p = leaf->portals[i];
+ pnum = p - portals;
+
+ // if some previous portal can't see it, skip
+ if ( !CheckBit( mightsee, pnum ) )
+ continue;
+
+ // if this portal can see some portals we mightsee, recurse
+ more = 0;
+ for (j=0 ; j<portallongs ; j++)
+ {
+ ((long *)newmight)[j] = ((long *)mightsee)[j]
+ & ((long *)p->portalflood)[j];
+ more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
+ }
+
+ if (!more)
+ continue; // can't see anything new
+
+ SetBit( cansee, pnum );
+
+ RecursiveLeafBitFlow (p->leaf, newmight, cansee);
+ }
+}
+
+/*
+==============
+BetterPortalVis
+==============
+*/
+void BetterPortalVis (int portalnum)
+{
+ portal_t *p;
+
+ p = portals+portalnum;
+
+ RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
+
+ // build leaf vis information
+ p->nummightsee = CountBits (p->portalvis, g_numportals*2);
+ c_vis += p->nummightsee;
+}
+
+
diff --git a/mp/src/utils/vvis/mpivis.cpp b/mp/src/utils/vvis/mpivis.cpp new file mode 100644 index 00000000..6da76ebc --- /dev/null +++ b/mp/src/utils/vvis/mpivis.cpp @@ -0,0 +1,640 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include <windows.h>
+#include "vis.h"
+#include "threads.h"
+#include "stdlib.h"
+#include "pacifier.h"
+#include "mpi_stats.h"
+#include "vmpi.h"
+#include "vmpi_dispatch.h"
+#include "vmpi_filesystem.h"
+#include "vmpi_distribute_work.h"
+#include "iphelpers.h"
+#include "threadhelpers.h"
+#include "vstdlib/random.h"
+#include "vmpi_tools_shared.h"
+#include <conio.h>
+#include "scratchpad_helpers.h"
+
+
+#define VMPI_VVIS_PACKET_ID 1
+ // Sub packet IDs.
+ #define VMPI_SUBPACKETID_DISCONNECT_NOTIFY 3 // We send ourselves this when there is a disconnect.
+ #define VMPI_SUBPACKETID_BASEPORTALVIS 5
+ #define VMPI_SUBPACKETID_PORTALFLOW 6
+ #define VMPI_BASEPORTALVIS_RESULTS 7
+ #define VMPI_BASEPORTALVIS_WORKER_DONE 8
+ #define VMPI_PORTALFLOW_RESULTS 9
+ #define VMPI_SUBPACKETID_BASEPORTALVIS_SYNC 11
+ #define VMPI_SUBPACKETID_PORTALFLOW_SYNC 12
+ #define VMPI_SUBPACKETID_MC_ADDR 13
+
+// DistributeWork owns this packet ID.
+#define VMPI_DISTRIBUTEWORK_PACKETID 2
+
+
+extern bool fastvis;
+
+// The worker waits until these are true.
+bool g_bBasePortalVisSync = false;
+bool g_bPortalFlowSync = false;
+
+CUtlVector<char> g_BasePortalVisResultsFilename;
+
+CCycleCount g_CPUTime;
+
+
+// This stuff is all for the multicast channel the master uses to send out the portal results.
+ISocket *g_pPortalMCSocket = NULL;
+CIPAddr g_PortalMCAddr;
+bool g_bGotMCAddr = false;
+HANDLE g_hMCThread = NULL;
+CEvent g_MCThreadExitEvent;
+unsigned long g_PortalMCThreadUniqueID = 0;
+int g_nMulticastPortalsReceived = 0;
+
+
+// Handle VVIS packets.
+bool VVIS_DispatchFn( MessageBuffer *pBuf, int iSource, int iPacketID )
+{
+ switch ( pBuf->data[1] )
+ {
+ case VMPI_SUBPACKETID_MC_ADDR:
+ {
+ pBuf->setOffset( 2 );
+ pBuf->read( &g_PortalMCAddr, sizeof( g_PortalMCAddr ) );
+ g_bGotMCAddr = true;
+ return true;
+ }
+
+ case VMPI_SUBPACKETID_DISCONNECT_NOTIFY:
+ {
+ // This is just used to cause nonblocking dispatches to jump out so loops like the one
+ // in AppBarrier can handle the fact that there are disconnects.
+ return true;
+ }
+
+ case VMPI_SUBPACKETID_BASEPORTALVIS_SYNC:
+ {
+ g_bBasePortalVisSync = true;
+ return true;
+ }
+
+ case VMPI_SUBPACKETID_PORTALFLOW_SYNC:
+ {
+ g_bPortalFlowSync = true;
+ return true;
+ }
+
+ case VMPI_BASEPORTALVIS_RESULTS:
+ {
+ const char *pFilename = &pBuf->data[2];
+ g_BasePortalVisResultsFilename.CopyArray( pFilename, strlen( pFilename ) + 1 );
+ return true;
+ }
+
+ default:
+ {
+ return false;
+ }
+ }
+}
+CDispatchReg g_VVISDispatchReg( VMPI_VVIS_PACKET_ID, VVIS_DispatchFn ); // register to handle the messages we want
+CDispatchReg g_DistributeWorkReg( VMPI_DISTRIBUTEWORK_PACKETID, DistributeWorkDispatch );
+
+
+
+void VMPI_DeletePortalMCSocket()
+{
+ // Stop the thread if it exists.
+ if ( g_hMCThread )
+ {
+ g_MCThreadExitEvent.SetEvent();
+ WaitForSingleObject( g_hMCThread, INFINITE );
+ CloseHandle( g_hMCThread );
+ g_hMCThread = NULL;
+ }
+
+ if ( g_pPortalMCSocket )
+ {
+ g_pPortalMCSocket->Release();
+ g_pPortalMCSocket = NULL;
+ }
+}
+
+
+void VVIS_SetupMPI( int &argc, char **&argv )
+{
+ if ( !VMPI_FindArg( argc, argv, "-mpi", "" ) && !VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Worker ), "" ) )
+ return;
+
+ CmdLib_AtCleanup( VMPI_Stats_Term );
+ CmdLib_AtCleanup( VMPI_DeletePortalMCSocket );
+
+ VMPI_Stats_InstallSpewHook();
+
+ // Force local mode?
+ VMPIRunMode mode;
+ if ( VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Local ), "" ) )
+ mode = VMPI_RUN_LOCAL;
+ else
+ mode = VMPI_RUN_NETWORKED;
+
+ //
+ // Extract mpi specific arguments
+ //
+ Msg( "Initializing VMPI...\n" );
+ if ( !VMPI_Init( argc, argv, "dependency_info_vvis.txt", HandleMPIDisconnect, mode ) )
+ {
+ Error( "MPI_Init failed." );
+ }
+
+ StatsDB_InitStatsDatabase( argc, argv, "dbinfo_vvis.txt" );
+}
+
+
+void ProcessBasePortalVis( int iThread, uint64 iPortal, MessageBuffer *pBuf )
+{
+ CTimeAdder adder( &g_CPUTime );
+
+ BasePortalVis( iThread, iPortal );
+
+ // Send my result to the master
+ if ( pBuf )
+ {
+ portal_t * p = &portals[iPortal];
+ pBuf->write( p->portalfront, portalbytes );
+ pBuf->write( p->portalflood, portalbytes );
+ }
+}
+
+
+void ReceiveBasePortalVis( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ portal_t * p = &portals[iWorkUnit];
+ if ( p->portalflood != 0 || p->portalfront != 0 || p->portalvis != 0)
+ {
+ Msg("Duplicate portal %llu\n", iWorkUnit);
+ }
+
+ if ( pBuf->getLen() - pBuf->getOffset() != portalbytes*2 )
+ Error( "Invalid packet in ReceiveBasePortalVis." );
+
+ //
+ // allocate memory for bitwise vis solutions for this portal
+ //
+ p->portalfront = (byte*)malloc (portalbytes);
+ pBuf->read( p->portalfront, portalbytes );
+
+ p->portalflood = (byte*)malloc (portalbytes);
+ pBuf->read( p->portalflood, portalbytes );
+
+ p->portalvis = (byte*)malloc (portalbytes);
+ memset (p->portalvis, 0, portalbytes);
+
+ p->nummightsee = CountBits( p->portalflood, g_numportals*2 );
+}
+
+
+//-----------------------------------------
+//
+// Run BasePortalVis across all available processing nodes
+// Then collect and redistribute the results.
+//
+void RunMPIBasePortalVis()
+{
+ int i;
+
+ Msg( "\n\nportalbytes: %d\nNum Work Units: %d\nTotal data size: %d\n", portalbytes, g_numportals*2, portalbytes*g_numportals*2 );
+ Msg("%-20s ", "BasePortalVis:");
+ if ( g_bMPIMaster )
+ StartPacifier("");
+
+
+ VMPI_SetCurrentStage( "RunMPIBasePortalVis" );
+
+ // Note: we're aiming for about 1500 portals in a map, so about 3000 work units.
+ g_CPUTime.Init();
+ double elapsed = DistributeWork(
+ g_numportals * 2, // # work units
+ VMPI_DISTRIBUTEWORK_PACKETID, // packet ID
+ ProcessBasePortalVis, // Worker function to process work units
+ ReceiveBasePortalVis // Master function to receive work results
+ );
+
+ if ( g_bMPIMaster )
+ {
+ EndPacifier( false );
+ Msg( " (%d)\n", (int)elapsed );
+ }
+
+ //
+ // Distribute the results to all the workers.
+ //
+ if ( g_bMPIMaster )
+ {
+ if ( !fastvis )
+ {
+ VMPI_SetCurrentStage( "SendPortalResults" );
+
+ // Store all the portal results in a temp file and multicast that to the workers.
+ CUtlVector<char> allPortalData;
+ allPortalData.SetSize( g_numportals * 2 * portalbytes * 2 );
+
+ char *pOut = allPortalData.Base();
+ for ( i=0; i < g_numportals * 2; i++)
+ {
+ portal_t *p = &portals[i];
+
+ memcpy( pOut, p->portalfront, portalbytes );
+ pOut += portalbytes;
+
+ memcpy( pOut, p->portalflood, portalbytes );
+ pOut += portalbytes;
+ }
+
+ const char *pVirtualFilename = "--portal-results--";
+ VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, allPortalData.Base(), allPortalData.Count() );
+
+ char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_BASEPORTALVIS_RESULTS };
+ VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT );
+ }
+ }
+ else
+ {
+ VMPI_SetCurrentStage( "RecvPortalResults" );
+
+ // Wait until we've received the filename from the master.
+ while ( g_BasePortalVisResultsFilename.Count() == 0 )
+ {
+ VMPI_DispatchNextMessage();
+ }
+
+ // Open
+ FileHandle_t fp = g_pFileSystem->Open( g_BasePortalVisResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID );
+ if ( !fp )
+ Error( "Can't open '%s' to read portal info.", g_BasePortalVisResultsFilename.Base() );
+
+ for ( i=0; i < g_numportals * 2; i++)
+ {
+ portal_t *p = &portals[i];
+
+ p->portalfront = (byte*)malloc (portalbytes);
+ g_pFileSystem->Read( p->portalfront, portalbytes, fp );
+
+ p->portalflood = (byte*)malloc (portalbytes);
+ g_pFileSystem->Read( p->portalflood, portalbytes, fp );
+
+ p->portalvis = (byte*)malloc (portalbytes);
+ memset (p->portalvis, 0, portalbytes);
+
+ p->nummightsee = CountBits (p->portalflood, g_numportals*2);
+ }
+
+ g_pFileSystem->Close( fp );
+ }
+
+
+ if ( !g_bMPIMaster )
+ {
+ if ( g_iVMPIVerboseLevel >= 1 )
+ Msg( "\n%% worker CPU utilization during BasePortalVis: %.1f\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
+ }
+}
+
+
+
+void ProcessPortalFlow( int iThread, uint64 iPortal, MessageBuffer *pBuf )
+{
+ // Process Portal and distribute results
+ CTimeAdder adder( &g_CPUTime );
+
+ PortalFlow( iThread, iPortal );
+
+ // Send my result to root and potentially the other slaves
+ // The slave results are read in RecursiveLeafFlow
+ //
+ if ( pBuf )
+ {
+ portal_t * p = sorted_portals[iPortal];
+ pBuf->write( p->portalvis, portalbytes );
+ }
+}
+
+
+void ReceivePortalFlow( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker )
+{
+ portal_t *p = sorted_portals[iWorkUnit];
+
+ if ( p->status != stat_done )
+ {
+ pBuf->read( p->portalvis, portalbytes );
+ p->status = stat_done;
+
+
+ // Multicast the status of this portal out.
+ if ( g_pPortalMCSocket )
+ {
+ char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_PORTALFLOW_RESULTS };
+ void *chunks[4] = { cPacketID, &g_PortalMCThreadUniqueID, &iWorkUnit, p->portalvis };
+ int chunkLengths[4] = { sizeof( cPacketID ), sizeof( g_PortalMCThreadUniqueID ), sizeof( iWorkUnit ), portalbytes };
+
+ g_pPortalMCSocket->SendChunksTo( &g_PortalMCAddr, chunks, chunkLengths, ARRAYSIZE( chunks ) );
+ }
+ }
+}
+
+
+DWORD WINAPI PortalMCThreadFn( LPVOID p )
+{
+ CUtlVector<char> data;
+ data.SetSize( portalbytes + 128 );
+
+ DWORD waitTime = 0;
+ while ( WaitForSingleObject( g_MCThreadExitEvent.GetEventHandle(), waitTime ) != WAIT_OBJECT_0 )
+ {
+ CIPAddr ipFrom;
+ int len = g_pPortalMCSocket->RecvFrom( data.Base(), data.Count(), &ipFrom );
+ if ( len == -1 )
+ {
+ waitTime = 20;
+ }
+ else
+ {
+ // These lengths must match exactly what is sent in ReceivePortalFlow.
+ if ( len == 2 + sizeof( g_PortalMCThreadUniqueID ) + sizeof( int ) + portalbytes )
+ {
+ // Perform more validation...
+ if ( data[0] == VMPI_VVIS_PACKET_ID && data[1] == VMPI_PORTALFLOW_RESULTS )
+ {
+ if ( *((unsigned long*)&data[2]) == g_PortalMCThreadUniqueID )
+ {
+ int iWorkUnit = *((int*)&data[6]);
+ if ( iWorkUnit >= 0 && iWorkUnit < g_numportals*2 )
+ {
+ portal_t *p = sorted_portals[iWorkUnit];
+ if ( p )
+ {
+ ++g_nMulticastPortalsReceived;
+ memcpy( p->portalvis, &data[10], portalbytes );
+ p->status = stat_done;
+ waitTime = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+void MCThreadCleanupFn()
+{
+ g_MCThreadExitEvent.SetEvent();
+}
+
+
+// --------------------------------------------------------------------------------- //
+// Cheesy hack to let them stop the job early and keep the results of what has
+// been done so far.
+// --------------------------------------------------------------------------------- //
+
+class CVisDistributeWorkCallbacks : public IWorkUnitDistributorCallbacks
+{
+public:
+ CVisDistributeWorkCallbacks()
+ {
+ m_bExitedEarly = false;
+ m_iState = STATE_NONE;
+ }
+
+ virtual bool Update()
+ {
+ if ( kbhit() )
+ {
+ int key = toupper( getch() );
+ if ( m_iState == STATE_NONE )
+ {
+ if ( key == 'M' )
+ {
+ m_iState = STATE_AT_MENU;
+ Warning("\n\n"
+ "----------------------\n"
+ "1. Write scratchpad file.\n"
+ "2. Exit early and use fast vis for remaining portals.\n"
+ "\n"
+ "0. Exit menu.\n"
+ "----------------------\n"
+ "\n"
+ );
+ }
+ }
+ else if ( m_iState == STATE_AT_MENU )
+ {
+ if ( key == '1' )
+ {
+ Warning(
+ "\n"
+ "\nWriting scratchpad file."
+ "\nCommand line: scratchpad3dviewer -file scratch.pad\n"
+ "\nRed portals are the portals that are fast vis'd."
+ "\n"
+ );
+ m_iState = STATE_NONE;
+ IScratchPad3D *pPad = ScratchPad3D_Create( "scratch.pad" );
+ if ( pPad )
+ {
+ ScratchPad_DrawWorld( pPad, false );
+
+ // Draw the portals that haven't been vis'd.
+ for ( int i=0; i < g_numportals*2; i++ )
+ {
+ portal_t *p = sorted_portals[i];
+ ScratchPad_DrawWinding( pPad, p->winding->numpoints, p->winding->points, Vector( 1, 0, 0 ), Vector( .3, .3, .3 ) );
+ }
+
+ pPad->Release();
+ }
+ }
+ else if ( key == '2' )
+ {
+ // Exit the process early.
+ m_bExitedEarly = true;
+ return true;
+ }
+ else if ( key == '0' )
+ {
+ m_iState = STATE_NONE;
+ Warning( "\n\nExited menu.\n\n" );
+ }
+ }
+ }
+
+ return false;
+ }
+
+public:
+ enum
+ {
+ STATE_NONE,
+ STATE_AT_MENU
+ };
+
+ bool m_bExitedEarly;
+ int m_iState; // STATE_ enum.
+};
+
+
+CVisDistributeWorkCallbacks g_VisDistributeWorkCallbacks;
+
+
+void CheckExitedEarly()
+{
+ if ( g_VisDistributeWorkCallbacks.m_bExitedEarly )
+ {
+ Warning( "\nExited early, using fastvis results...\n" );
+ Warning( "Exited early, using fastvis results...\n" );
+
+ // Use the fastvis results for portals that we didn't get results for.
+ for ( int i=0; i < g_numportals*2; i++ )
+ {
+ if ( sorted_portals[i]->status != stat_done )
+ {
+ sorted_portals[i]->portalvis = sorted_portals[i]->portalflood;
+ sorted_portals[i]->status = stat_done;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------
+//
+// Run PortalFlow across all available processing nodes
+//
+void RunMPIPortalFlow()
+{
+ Msg( "%-20s ", "MPIPortalFlow:" );
+ if ( g_bMPIMaster )
+ StartPacifier("");
+
+ // Workers wait until we get the MC socket address.
+ g_PortalMCThreadUniqueID = StatsDB_GetUniqueJobID();
+ if ( g_bMPIMaster )
+ {
+ CCycleCount cnt;
+ cnt.Sample();
+ CUniformRandomStream randomStream;
+ randomStream.SetSeed( cnt.GetMicroseconds() );
+
+ g_PortalMCAddr.port = randomStream.RandomInt( 22000, 25000 ); // Pulled out of something else.
+ g_PortalMCAddr.ip[0] = (unsigned char)RandomInt( 225, 238 );
+ g_PortalMCAddr.ip[1] = (unsigned char)RandomInt( 0, 255 );
+ g_PortalMCAddr.ip[2] = (unsigned char)RandomInt( 0, 255 );
+ g_PortalMCAddr.ip[3] = (unsigned char)RandomInt( 3, 255 );
+
+ g_pPortalMCSocket = CreateIPSocket();
+ int i=0;
+ for ( i; i < 5; i++ )
+ {
+ if ( g_pPortalMCSocket->BindToAny( randomStream.RandomInt( 20000, 30000 ) ) )
+ break;
+ }
+ if ( i == 5 )
+ {
+ Error( "RunMPIPortalFlow: can't open a socket to multicast on." );
+ }
+
+ char cPacketID[2] = { VMPI_VVIS_PACKET_ID, VMPI_SUBPACKETID_MC_ADDR };
+ VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), &g_PortalMCAddr, sizeof( g_PortalMCAddr ), VMPI_PERSISTENT );
+ }
+ else
+ {
+ VMPI_SetCurrentStage( "wait for MC address" );
+
+ while ( !g_bGotMCAddr )
+ {
+ VMPI_DispatchNextMessage();
+ }
+
+ // Open our multicast receive socket.
+ g_pPortalMCSocket = CreateMulticastListenSocket( g_PortalMCAddr );
+ if ( !g_pPortalMCSocket )
+ {
+ char err[512];
+ IP_GetLastErrorString( err, sizeof( err ) );
+ Error( "RunMPIPortalFlow: CreateMulticastListenSocket failed. (%s).", err );
+ }
+
+ // Make a thread to listen for the data on the multicast socket.
+ DWORD dwDummy = 0;
+ g_MCThreadExitEvent.Init( false, false );
+
+ // Make sure we kill the MC thread if the app exits ungracefully.
+ CmdLib_AtCleanup( MCThreadCleanupFn );
+
+ g_hMCThread = CreateThread(
+ NULL,
+ 0,
+ PortalMCThreadFn,
+ NULL,
+ 0,
+ &dwDummy );
+
+ if ( !g_hMCThread )
+ {
+ Error( "RunMPIPortalFlow: CreateThread failed for multicast receive thread." );
+ }
+ }
+
+ VMPI_SetCurrentStage( "RunMPIBasePortalFlow" );
+
+
+ g_pDistributeWorkCallbacks = &g_VisDistributeWorkCallbacks;
+
+ g_CPUTime.Init();
+ double elapsed = DistributeWork(
+ g_numportals * 2, // # work units
+ VMPI_DISTRIBUTEWORK_PACKETID, // packet ID
+ ProcessPortalFlow, // Worker function to process work units
+ ReceivePortalFlow // Master function to receive work results
+ );
+
+ g_pDistributeWorkCallbacks = NULL;
+
+ CheckExitedEarly();
+
+ // Stop the multicast stuff.
+ VMPI_DeletePortalMCSocket();
+
+ if( !g_bMPIMaster )
+ {
+ if ( g_iVMPIVerboseLevel >= 1 )
+ {
+ Msg( "Received %d (out of %d) portals from multicast.\n", g_nMulticastPortalsReceived, g_numportals * 2 );
+ Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads );
+ }
+
+ Msg( "VVIS worker finished. Over and out.\n" );
+ VMPI_SetCurrentStage( "worker done" );
+
+ CmdLib_Exit( 0 );
+ }
+
+ if ( g_bMPIMaster )
+ {
+ EndPacifier( false );
+ Msg( " (%d)\n", (int)elapsed );
+ }
+}
+
diff --git a/mp/src/utils/vvis/mpivis.h b/mp/src/utils/vvis/mpivis.h new file mode 100644 index 00000000..a6ee349e --- /dev/null +++ b/mp/src/utils/vvis/mpivis.h @@ -0,0 +1,21 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MPIVIS_H
+#define MPIVIS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void VVIS_SetupMPI( int &argc, char **&argv );
+
+
+void RunMPIBasePortalVis();
+void RunMPIPortalFlow();
+
+
+#endif // MPIVIS_H
diff --git a/mp/src/utils/vvis/vis.h b/mp/src/utils/vvis/vis.h new file mode 100644 index 00000000..c4b58414 --- /dev/null +++ b/mp/src/utils/vvis/vis.h @@ -0,0 +1,125 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vis.h
+
+#include "cmdlib.h"
+#include "mathlib/mathlib.h"
+#include "bsplib.h"
+
+
+#define MAX_PORTALS 65536
+
+#define PORTALFILE "PRT1"
+
+extern bool g_bUseRadius; // prototyping TF2, "radius vis" solution
+extern double g_VisRadius; // the radius for the TF2 "radius vis"
+
+struct plane_t
+{
+ Vector normal;
+ float dist;
+};
+
+#define MAX_POINTS_ON_WINDING 64
+#define MAX_POINTS_ON_FIXED_WINDING 12
+
+struct winding_t
+{
+ qboolean original; // don't free, it's part of the portal
+ int numpoints;
+ Vector points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized
+};
+
+winding_t *NewWinding (int points);
+void FreeWinding (winding_t *w);
+winding_t *CopyWinding (winding_t *w);
+
+
+typedef enum {stat_none, stat_working, stat_done} vstatus_t;
+struct portal_t
+{
+ plane_t plane; // normal pointing into neighbor
+ int leaf; // neighbor
+
+ Vector origin; // for fast clip testing
+ float radius;
+
+ winding_t *winding;
+ vstatus_t status;
+ byte *portalfront; // [portals], preliminary
+ byte *portalflood; // [portals], intermediate
+ byte *portalvis; // [portals], final
+
+ int nummightsee; // bit count on portalflood for sort
+};
+
+struct leaf_t
+{
+ CUtlVector<portal_t *> portals;
+};
+
+
+struct pstack_t
+{
+ byte mightsee[MAX_PORTALS/8]; // bit string
+ pstack_t *next;
+ leaf_t *leaf;
+ portal_t *portal; // portal exiting
+ winding_t *source;
+ winding_t *pass;
+
+ winding_t windings[3]; // source, pass, temp in any order
+ int freewindings[3];
+
+ plane_t portalplane;
+};
+
+struct threaddata_t
+{
+ portal_t *base;
+ int c_chains;
+ pstack_t pstack_head;
+};
+
+extern int g_numportals;
+extern int portalclusters;
+
+extern portal_t *portals;
+extern leaf_t *leafs;
+
+extern int c_portaltest, c_portalpass, c_portalcheck;
+extern int c_portalskip, c_leafskip;
+extern int c_vistest, c_mighttest;
+extern int c_chains;
+
+extern byte *vismap, *vismap_p, *vismap_end; // past visfile
+
+extern int testlevel;
+
+extern byte *uncompressed;
+
+extern int leafbytes, leaflongs;
+extern int portalbytes, portallongs;
+
+
+void LeafFlow (int leafnum);
+
+
+void BasePortalVis (int iThread, int portalnum);
+void BetterPortalVis (int portalnum);
+void PortalFlow (int iThread, int portalnum);
+void WritePortalTrace( const char *source );
+
+extern portal_t *sorted_portals[MAX_MAP_PORTALS*2];
+extern int g_TraceClusterStart, g_TraceClusterStop;
+
+int CountBits (byte *bits, int numbits);
+
+#define CheckBit( bitstring, bitNumber ) ( (bitstring)[ ((bitNumber) >> 3) ] & ( 1 << ( (bitNumber) & 7 ) ) )
+#define SetBit( bitstring, bitNumber ) ( (bitstring)[ ((bitNumber) >> 3) ] |= ( 1 << ( (bitNumber) & 7 ) ) )
+#define ClearBit( bitstring, bitNumber ) ( (bitstring)[ ((bitNumber) >> 3) ] &= ~( 1 << ( (bitNumber) & 7 ) ) )
diff --git a/mp/src/utils/vvis/vvis.cpp b/mp/src/utils/vvis/vvis.cpp new file mode 100644 index 00000000..577c1cc3 --- /dev/null +++ b/mp/src/utils/vvis/vvis.cpp @@ -0,0 +1,1240 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vis.c
+
+#include <windows.h>
+#include "vis.h"
+#include "threads.h"
+#include "stdlib.h"
+#include "pacifier.h"
+#include "vmpi.h"
+#include "mpivis.h"
+#include "tier1/strtools.h"
+#include "collisionutils.h"
+#include "tier0/icommandline.h"
+#include "vmpi_tools_shared.h"
+#include "ilaunchabledll.h"
+#include "tools_minidump.h"
+#include "loadcmdline.h"
+#include "byteswap.h"
+
+
+int g_numportals;
+int portalclusters;
+
+char inbase[32];
+
+portal_t *portals;
+leaf_t *leafs;
+
+int c_portaltest, c_portalpass, c_portalcheck;
+
+byte *uncompressedvis;
+
+byte *vismap, *vismap_p, *vismap_end; // past visfile
+int originalvismapsize;
+
+int leafbytes; // (portalclusters+63)>>3
+int leaflongs;
+
+int portalbytes, portallongs;
+
+bool fastvis;
+bool nosort;
+
+int totalvis;
+
+portal_t *sorted_portals[MAX_MAP_PORTALS*2];
+
+bool g_bUseRadius = false;
+double g_VisRadius = 4096.0f * 4096.0f;
+
+bool g_bLowPriority = false;
+
+//=============================================================================
+
+void PlaneFromWinding (winding_t *w, plane_t *plane)
+{
+ Vector v1, v2;
+
+// calc plane
+ VectorSubtract (w->points[2], w->points[1], v1);
+ VectorSubtract (w->points[0], w->points[1], v2);
+ CrossProduct (v2, v1, plane->normal);
+ VectorNormalize (plane->normal);
+ plane->dist = DotProduct (w->points[0], plane->normal);
+}
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+ winding_t *w;
+ int size;
+
+ if (points > MAX_POINTS_ON_WINDING)
+ Error ("NewWinding: %i points, max %d", points, MAX_POINTS_ON_WINDING);
+
+ size = (int)(&((winding_t *)0)->points[points]);
+ w = (winding_t*)malloc (size);
+ memset (w, 0, size);
+
+ return w;
+}
+
+void pw(winding_t *w)
+{
+ int i;
+ for (i=0 ; i<w->numpoints ; i++)
+ Msg ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
+}
+
+void prl(leaf_t *l)
+{
+ int i;
+ portal_t *p;
+ plane_t pl;
+
+ int count = l->portals.Count();
+ for (i=0 ; i<count ; i++)
+ {
+ p = l->portals[i];
+ pl = p->plane;
+ Msg ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
+ }
+}
+
+
+//=============================================================================
+
+/*
+=============
+SortPortals
+
+Sorts the portals from the least complex, so the later ones can reuse
+the earlier information.
+=============
+*/
+int PComp (const void *a, const void *b)
+{
+ if ( (*(portal_t **)a)->nummightsee == (*(portal_t **)b)->nummightsee)
+ return 0;
+ if ( (*(portal_t **)a)->nummightsee < (*(portal_t **)b)->nummightsee)
+ return -1;
+
+ return 1;
+}
+
+void BuildTracePortals( int clusterStart )
+{
+ leaf_t *leaf = &leafs[g_TraceClusterStart];
+ g_numportals = leaf->portals.Count();
+ for ( int i = 0; i < g_numportals; i++ )
+ {
+ sorted_portals[i] = leaf->portals[i];
+ }
+}
+
+void SortPortals (void)
+{
+ int i;
+
+ for (i=0 ; i<g_numportals*2 ; i++)
+ sorted_portals[i] = &portals[i];
+
+ if (nosort)
+ return;
+ qsort (sorted_portals, g_numportals*2, sizeof(sorted_portals[0]), PComp);
+}
+
+
+/*
+==============
+LeafVectorFromPortalVector
+==============
+*/
+int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
+{
+ int i;
+ portal_t *p;
+ int c_leafs;
+
+
+ memset (leafbits, 0, leafbytes);
+
+ for (i=0 ; i<g_numportals*2 ; i++)
+ {
+ if ( CheckBit( portalbits, i ) )
+ {
+ p = portals+i;
+ SetBit( leafbits, p->leaf );
+ }
+ }
+
+ c_leafs = CountBits (leafbits, portalclusters);
+
+ return c_leafs;
+}
+
+
+/*
+===============
+ClusterMerge
+
+Merges the portal visibility for a leaf
+===============
+*/
+void ClusterMerge (int clusternum)
+{
+ leaf_t *leaf;
+// byte portalvector[MAX_PORTALS/8];
+ byte portalvector[MAX_PORTALS/4]; // 4 because portal bytes is * 2
+ byte uncompressed[MAX_MAP_LEAFS/8];
+ int i, j;
+ int numvis;
+ portal_t *p;
+ int pnum;
+
+ // OR together all the portalvis bits
+
+ memset (portalvector, 0, portalbytes);
+ leaf = &leafs[clusternum];
+ for (i=0 ; i < leaf->portals.Count(); i++)
+ {
+ p = leaf->portals[i];
+ if (p->status != stat_done)
+ Error ("portal not done %d %p %p\n", i, p, portals);
+ for (j=0 ; j<portallongs ; j++)
+ ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
+ pnum = p - portals;
+ SetBit( portalvector, pnum );
+ }
+
+ // convert portal bits to leaf bits
+ numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
+
+#if 0
+ // func_viscluster makes this happen all the time because it allows a non-convex set of portals
+ // My analysis says this is ok, but it does make this check for errors in vis kind of useless
+ if ( CheckBit( uncompressed, clusternum ) )
+ Warning("WARNING: Cluster portals saw into cluster\n");
+#endif
+
+ SetBit( uncompressed, clusternum );
+ numvis++; // count the leaf itself
+
+ // save uncompressed for PHS calculation
+ memcpy (uncompressedvis + clusternum*leafbytes, uncompressed, leafbytes);
+
+ qprintf ("cluster %4i : %4i visible\n", clusternum, numvis);
+ totalvis += numvis;
+}
+
+static int CompressAndCrosscheckClusterVis( int clusternum )
+{
+ int optimized = 0;
+ byte compressed[MAX_MAP_LEAFS/8];
+//
+// compress the bit string
+//
+ byte *uncompressed = uncompressedvis + clusternum*leafbytes;
+ for ( int i = 0; i < portalclusters; i++ )
+ {
+ if ( i == clusternum )
+ continue;
+
+ if ( CheckBit( uncompressed, i ) )
+ {
+ byte *other = uncompressedvis + i*leafbytes;
+ if ( !CheckBit( other, clusternum ) )
+ {
+ ClearBit( uncompressed, i );
+ optimized++;
+ }
+ }
+ }
+ int numbytes = CompressVis( uncompressed, compressed );
+
+ byte *dest = vismap_p;
+ vismap_p += numbytes;
+
+ if (vismap_p > vismap_end)
+ Error ("Vismap expansion overflow");
+
+ dvis->bitofs[clusternum][DVIS_PVS] = dest-vismap;
+
+ memcpy( dest, compressed, numbytes );
+
+ // check vis data
+ DecompressVis( vismap + dvis->bitofs[clusternum][DVIS_PVS], compressed );
+
+ return optimized;
+}
+
+
+/*
+==================
+CalcPortalVis
+==================
+*/
+void CalcPortalVis (void)
+{
+ int i;
+
+ // fastvis just uses mightsee for a very loose bound
+ if( fastvis )
+ {
+ for (i=0 ; i<g_numportals*2 ; i++)
+ {
+ portals[i].portalvis = portals[i].portalflood;
+ portals[i].status = stat_done;
+ }
+ return;
+ }
+
+
+ if (g_bUseMPI)
+ {
+ RunMPIPortalFlow();
+ }
+ else
+ {
+ RunThreadsOnIndividual (g_numportals*2, true, PortalFlow);
+ }
+}
+
+
+void CalcVisTrace (void)
+{
+ RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
+ BuildTracePortals( g_TraceClusterStart );
+ // NOTE: We only schedule the one-way portals out of the start cluster here
+ // so don't run g_numportals*2 in this case
+ RunThreadsOnIndividual (g_numportals, true, PortalFlow);
+}
+
+/*
+==================
+CalcVis
+==================
+*/
+void CalcVis (void)
+{
+ int i;
+
+ if (g_bUseMPI)
+ {
+ RunMPIBasePortalVis();
+ }
+ else
+ {
+ RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
+ }
+
+ SortPortals ();
+
+ CalcPortalVis ();
+
+ //
+ // assemble the leaf vis lists by oring the portal lists
+ //
+ for ( i = 0; i < portalclusters; i++ )
+ {
+ ClusterMerge( i );
+ }
+
+ int count = 0;
+ // Now crosscheck each leaf's vis and compress
+ for ( i = 0; i < portalclusters; i++ )
+ {
+ count += CompressAndCrosscheckClusterVis( i );
+ }
+
+
+ Msg ("Optimized: %d visible clusters (%.2f%%)\n", count, count*100.0/totalvis);
+ Msg ("Total clusters visible: %i\n", totalvis);
+ Msg ("Average clusters visible: %i\n", totalvis / portalclusters);
+}
+
+
+void SetPortalSphere (portal_t *p)
+{
+ int i;
+ Vector total, dist;
+ winding_t *w;
+ float r, bestr;
+
+ w = p->winding;
+ VectorCopy (vec3_origin, total);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorAdd (total, w->points[i], total);
+ }
+
+ for (i=0 ; i<3 ; i++)
+ total[i] /= w->numpoints;
+
+ bestr = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->points[i], total, dist);
+ r = VectorLength (dist);
+ if (r > bestr)
+ bestr = r;
+ }
+ VectorCopy (total, p->origin);
+ p->radius = bestr;
+}
+
+/*
+============
+LoadPortals
+============
+*/
+void LoadPortals (char *name)
+{
+ int i, j;
+ portal_t *p;
+ leaf_t *l;
+ char magic[80];
+ int numpoints;
+ winding_t *w;
+ int leafnums[2];
+ plane_t plane;
+
+ FILE *f;
+
+ // Open the portal file.
+ if ( g_bUseMPI )
+ {
+ // If we're using MPI, copy off the file to a temporary first. This will download the file
+ // from the MPI master, then we get to use nice functions like fscanf on it.
+ char tempPath[MAX_PATH], tempFile[MAX_PATH];
+ if ( GetTempPath( sizeof( tempPath ), tempPath ) == 0 )
+ {
+ Error( "LoadPortals: GetTempPath failed.\n" );
+ }
+
+ if ( GetTempFileName( tempPath, "vvis_portal_", 0, tempFile ) == 0 )
+ {
+ Error( "LoadPortals: GetTempFileName failed.\n" );
+ }
+
+ // Read all the data from the network file into memory.
+ FileHandle_t hFile = g_pFileSystem->Open(name, "r");
+ if ( hFile == FILESYSTEM_INVALID_HANDLE )
+ Error( "LoadPortals( %s ): couldn't get file from master.\n", name );
+
+ CUtlVector<char> data;
+ data.SetSize( g_pFileSystem->Size( hFile ) );
+ g_pFileSystem->Read( data.Base(), data.Count(), hFile );
+ g_pFileSystem->Close( hFile );
+
+ // Dump it into a temp file.
+ f = fopen( tempFile, "wt" );
+ fwrite( data.Base(), 1, data.Count(), f );
+ fclose( f );
+
+ // Open the temp file up.
+ f = fopen( tempFile, "rSTD" ); // read only, sequential, temporary, delete on close
+ }
+ else
+ {
+ f = fopen( name, "r" );
+ }
+
+ if ( !f )
+ Error ("LoadPortals: couldn't read %s\n",name);
+
+ if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalclusters, &g_numportals) != 3)
+ Error ("LoadPortals %s: failed to read header", name);
+ if (stricmp(magic,PORTALFILE))
+ Error ("LoadPortals %s: not a portal file", name);
+
+ Msg ("%4i portalclusters\n", portalclusters);
+ Msg ("%4i numportals\n", g_numportals);
+
+ if (g_numportals * 2 >= MAX_PORTALS)
+ {
+ Error("The map overflows the max portal count (%d of max %d)!\n", g_numportals, MAX_PORTALS / 2 );
+ }
+
+ // these counts should take advantage of 64 bit systems automatically
+ leafbytes = ((portalclusters+63)&~63)>>3;
+ leaflongs = leafbytes/sizeof(long);
+
+ portalbytes = ((g_numportals*2+63)&~63)>>3;
+ portallongs = portalbytes/sizeof(long);
+
+// each file portal is split into two memory portals
+ portals = (portal_t*)malloc(2*g_numportals*sizeof(portal_t));
+ memset (portals, 0, 2*g_numportals*sizeof(portal_t));
+
+ leafs = (leaf_t*)malloc(portalclusters*sizeof(leaf_t));
+ memset (leafs, 0, portalclusters*sizeof(leaf_t));
+
+ originalvismapsize = portalclusters*leafbytes;
+ uncompressedvis = (byte*)malloc(originalvismapsize);
+
+ vismap = vismap_p = dvisdata;
+ dvis->numclusters = portalclusters;
+ vismap_p = (byte *)&dvis->bitofs[portalclusters];
+
+ vismap_end = vismap + MAX_MAP_VISIBILITY;
+
+ for (i=0, p=portals ; i<g_numportals ; i++)
+ {
+ if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
+ != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ if (numpoints > MAX_POINTS_ON_WINDING)
+ Error ("LoadPortals: portal %i has too many points", i);
+ if ( (unsigned)leafnums[0] > portalclusters
+ || (unsigned)leafnums[1] > portalclusters)
+ Error ("LoadPortals: reading portal %i", i);
+
+ w = p->winding = NewWinding (numpoints);
+ w->original = true;
+ w->numpoints = numpoints;
+
+ for (j=0 ; j<numpoints ; j++)
+ {
+ double v[3];
+ int k;
+
+ // scanf into double, then assign to vec_t
+ // so we don't care what size vec_t is
+ if (fscanf (f, "(%lf %lf %lf ) "
+ , &v[0], &v[1], &v[2]) != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ for (k=0 ; k<3 ; k++)
+ w->points[j][k] = v[k];
+ }
+ fscanf (f, "\n");
+
+ // calc plane
+ PlaneFromWinding (w, &plane);
+
+ // create forward portal
+ l = &leafs[leafnums[0]];
+ l->portals.AddToTail(p);
+
+ p->winding = w;
+ VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
+ p->plane.dist = -plane.dist;
+ p->leaf = leafnums[1];
+ SetPortalSphere (p);
+ p++;
+
+ // create backwards portal
+ l = &leafs[leafnums[1]];
+ l->portals.AddToTail(p);
+
+ p->winding = NewWinding(w->numpoints);
+ p->winding->numpoints = w->numpoints;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
+ }
+
+ p->plane = plane;
+ p->leaf = leafnums[0];
+ SetPortalSphere (p);
+ p++;
+
+ }
+
+ fclose (f);
+}
+
+
+/*
+================
+CalcPAS
+
+Calculate the PAS (Potentially Audible Set)
+by ORing together all the PVS visible from a leaf
+================
+*/
+void CalcPAS (void)
+{
+ int i, j, k, l, index;
+ int bitbyte;
+ long *dest, *src;
+ byte *scan;
+ int count;
+ byte uncompressed[MAX_MAP_LEAFS/8];
+ byte compressed[MAX_MAP_LEAFS/8];
+
+ Msg ("Building PAS...\n");
+
+ count = 0;
+ for (i=0 ; i<portalclusters ; i++)
+ {
+ scan = uncompressedvis + i*leafbytes;
+ memcpy (uncompressed, scan, leafbytes);
+ for (j=0 ; j<leafbytes ; j++)
+ {
+ bitbyte = scan[j];
+ if (!bitbyte)
+ continue;
+ for (k=0 ; k<8 ; k++)
+ {
+ if (! (bitbyte & (1<<k)) )
+ continue;
+ // OR this pvs row into the phs
+ index = ((j<<3)+k);
+ if (index >= portalclusters)
+ Error ("Bad bit in PVS"); // pad bits should be 0
+ src = (long *)(uncompressedvis + index*leafbytes);
+ dest = (long *)uncompressed;
+ for (l=0 ; l<leaflongs ; l++)
+ ((long *)uncompressed)[l] |= src[l];
+ }
+ }
+ for (j=0 ; j<portalclusters ; j++)
+ {
+ if ( CheckBit( uncompressed, j ) )
+ {
+ count++;
+ }
+ }
+
+ //
+ // compress the bit string
+ //
+ j = CompressVis (uncompressed, compressed);
+
+ dest = (long *)vismap_p;
+ vismap_p += j;
+
+ if (vismap_p > vismap_end)
+ Error ("Vismap expansion overflow");
+
+ dvis->bitofs[i][DVIS_PAS] = (byte *)dest-vismap;
+
+ memcpy (dest, compressed, j);
+ }
+
+ Msg ("Average clusters audible: %i\n", count/portalclusters);
+}
+
+
+
+static void GetBoundsForFace( int faceID, Vector &faceMin, Vector &faceMax )
+{
+ ClearBounds( faceMin, faceMax );
+ dface_t *pFace = &dfaces[faceID];
+ int i;
+ for( i = pFace->firstedge; i < pFace->firstedge + pFace->numedges; i++ )
+ {
+ int edgeID = dsurfedges[i];
+ if( edgeID < 0 )
+ {
+ edgeID = -edgeID;
+ }
+ dedge_t *pEdge = &dedges[edgeID];
+ dvertex_t *pVert0 = &dvertexes[pEdge->v[0]];
+ dvertex_t *pVert1 = &dvertexes[pEdge->v[1]];
+ AddPointToBounds( pVert0->point, faceMin, faceMax );
+ AddPointToBounds( pVert1->point, faceMin, faceMax );
+ }
+}
+
+// FIXME: should stick this in mathlib
+static float GetMinDistanceBetweenBoundingBoxes( const Vector &min1, const Vector &max1,
+ const Vector &min2, const Vector &max2 )
+{
+ if( IsBoxIntersectingBox( min1, max1, min2, max2 ) )
+ {
+ return 0.0f;
+ }
+
+ Vector axisDist;
+ int i;
+ for( i = 0; i < 3; i++ )
+ {
+ if( min1[i] <= max2[i] && max1[i] >= min2[i] )
+ {
+ // the intersection in this dimension.
+ axisDist[i] = 0.0f;
+ }
+ else
+ {
+ float dist1, dist2;
+ dist1 = min1[i] - max2[i];
+ dist2 = min2[i] - max1[i];
+ axisDist[i] = dist1 > dist2 ? dist1 : dist2;
+ Assert( axisDist[i] > 0.0f );
+ }
+ }
+
+ float mag = axisDist.Length();
+ Assert( mag > 0.0f );
+ return mag;
+}
+
+static float CalcDistanceFromLeafToWater( int leafNum )
+{
+ byte uncompressed[MAX_MAP_LEAFS/8];
+
+ int j, k;
+
+ // If we know that this one doesn't see a water surface then don't bother doing anything.
+ if( ((dleafs[leafNum].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[leafNum].leafWaterDataID == -1 ) )
+ return 65535; // FIXME: make a define for this.
+
+ // First get the vis data..
+ int cluster = dleafs[leafNum].cluster;
+ if (cluster < 0)
+ return 65535; // FIXME: make a define for this.
+
+ DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
+
+ float minDist = 65535.0f; // FIXME: make a define for this.
+
+ Vector leafMin, leafMax;
+
+ leafMin[0] = ( float )dleafs[leafNum].mins[0];
+ leafMin[1] = ( float )dleafs[leafNum].mins[1];
+ leafMin[2] = ( float )dleafs[leafNum].mins[2];
+ leafMax[0] = ( float )dleafs[leafNum].maxs[0];
+ leafMax[1] = ( float )dleafs[leafNum].maxs[1];
+ leafMax[2] = ( float )dleafs[leafNum].maxs[2];
+
+/*
+ CUtlVector<listplane_t> temp;
+
+ // build a convex solid out of the planes so that we can get at the triangles.
+ for( j = dleafs[i].firstleafbrush; j < dleafs[i].firstleafbrush + dleafs[i].numleafbrushes; j++ )
+ {
+ dbrush_t *pBrush = &dbrushes[j];
+ for( k = pBrush->firstside; k < pBrush->firstside + pBrush->numsides; k++ )
+ {
+ dbrushside_t *pside = dbrushsides + k;
+ dplane_t *pplane = dplanes + pside->planenum;
+ AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist );
+ }
+ CPhysConvex *pConvex = physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), VPHYSICS_MERGE );
+ ConvertConvexToCollide( &pConvex,
+ temp.RemoveAll();
+ }
+*/
+
+ // Iterate over all potentially visible clusters from this leaf
+ for (j = 0; j < dvis->numclusters; ++j)
+ {
+ // Don't need to bother if this is the same as the current cluster
+ if (j == cluster)
+ continue;
+
+ // If the cluster isn't in our current pvs, then get out of here.
+ if ( !CheckBit( uncompressed, j ) )
+ continue;
+
+ // Found a visible cluster, now iterate over all leaves
+ // inside that cluster
+ for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
+ {
+ int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
+
+ // Don't bother testing the ones that don't see a water boundary.
+ if( ((dleafs[nClusterLeaf].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[nClusterLeaf].leafWaterDataID == -1 ) )
+ continue;
+
+ // Find the minimum distance between each surface on the boundary of the leaf
+ // that we have the pvs for and each water surface in the leaf that we are testing.
+ int nFirstFaceID = dleafs[nClusterLeaf].firstleafface;
+ for( int leafFaceID = 0; leafFaceID < dleafs[nClusterLeaf].numleaffaces; ++leafFaceID )
+ {
+ int faceID = dleaffaces[nFirstFaceID + leafFaceID];
+ dface_t *pFace = &dfaces[faceID];
+ if( pFace->texinfo == -1 )
+ continue;
+
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ if( pTexInfo->flags & SURF_WARP )
+ {
+ // Woo hoo!!! We found a water face.
+ // compare the bounding box of the face with the bounding
+ // box of the leaf that we are looking from and see
+ // what the closest distance is.
+ // FIXME: this could be a face/face distance between the water
+ // face and the bounding volume of the leaf.
+
+ // Get the bounding box of the face
+ Vector faceMin, faceMax;
+ GetBoundsForFace( faceID, faceMin, faceMax );
+ float dist = GetMinDistanceBetweenBoundingBoxes( leafMin, leafMax, faceMin, faceMax );
+ if( dist < minDist )
+ {
+ minDist = dist;
+ }
+ }
+ }
+ }
+ }
+ return minDist;
+}
+
+static void CalcDistanceFromLeavesToWater( void )
+{
+ int i;
+ for( i = 0; i < numleafs; i++ )
+ {
+ g_LeafMinDistToWater[i] = ( unsigned short )CalcDistanceFromLeafToWater( i );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Using the PVS, compute the visible fog volumes from each leaf
+//-----------------------------------------------------------------------------
+static void CalcVisibleFogVolumes()
+{
+ byte uncompressed[MAX_MAP_LEAFS/8];
+
+ int i, j, k;
+
+ // Clear the contents flags for water testing
+ for (i = 0; i < numleafs; ++i)
+ {
+ dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
+ g_LeafMinDistToWater[i] = 65535;
+ }
+
+ for (i = 0; i < numleafs; ++i)
+ {
+ // If we've already discovered that this leaf needs testing,
+ // no need to go through the work again...
+ if (dleafs[i].contents & CONTENTS_TESTFOGVOLUME)
+ {
+ Assert((dleafs[i].contents & (CONTENTS_SLIME | CONTENTS_WATER)) == 0);
+ continue;
+ }
+
+ // Don't bother checking fog volumes from solid leaves
+ if (dleafs[i].contents & CONTENTS_SOLID)
+ continue;
+
+ // Look only for leaves which are visible from leaves that have fluid in them.
+ if ( dleafs[i].leafWaterDataID == -1 )
+ continue;
+
+ // Don't bother about looking from CONTENTS_SLIME; we're not going to treat that as interesting.
+ // because slime is opaque
+ if ( dleafs[i].contents & CONTENTS_SLIME )
+ continue;
+
+ // First get the vis data..
+ int cluster = dleafs[i].cluster;
+ if (cluster < 0)
+ continue;
+
+ DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
+
+ // Iterate over all potentially visible clusters from this leaf
+ for (j = 0; j < dvis->numclusters; ++j)
+ {
+ // Don't need to bother if this is the same as the current cluster
+ if (j == cluster)
+ continue;
+
+ if ( !CheckBit( uncompressed, j ) )
+ continue;
+
+ // Found a visible cluster, now iterate over all leaves
+ // inside that cluster
+ for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
+ {
+ int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
+
+ // Don't bother checking fog volumes from solid leaves
+ if ( dleafs[nClusterLeaf].contents & CONTENTS_SOLID )
+ continue;
+
+ // Don't bother checking from any leaf that's got fluid in it
+ if ( dleafs[nClusterLeaf].leafWaterDataID != -1 )
+ continue;
+
+ // Here, we've found a case where a non-liquid leaf is visible from a liquid leaf
+ // So, in this case, we have to do the expensive test during rendering.
+ dleafs[nClusterLeaf].contents |= CONTENTS_TESTFOGVOLUME;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
+//-----------------------------------------------------------------------------
+float DetermineVisRadius( )
+{
+ float flRadius = -1;
+
+ // Check the max vis range to determine the vis radius
+ for (int i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!stricmp(pEntity, "env_fog_controller"))
+ {
+ flRadius = FloatForKey (&entities[i], "farz");
+ if (flRadius == 0.0f)
+ flRadius = -1.0f;
+ break;
+ }
+ }
+
+ return flRadius;
+}
+
+void MarkLeavesAsRadial()
+{
+ for ( int i = 0; i < numleafs; i++ )
+ {
+ dleafs[i].flags |= LEAF_FLAGS_RADIAL;
+ }
+}
+
+
+int ParseCommandLine( int argc, char **argv )
+{
+ int i;
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!Q_stricmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!Q_stricmp(argv[i], "-fast"))
+ {
+ Msg ("fastvis = true\n");
+ fastvis = true;
+ }
+ else if (!Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose"))
+ {
+ Msg ("verbose = true\n");
+ verbose = true;
+ }
+ else if( !Q_stricmp( argv[i], "-radius_override" ) )
+ {
+ g_bUseRadius = true;
+ g_VisRadius = atof( argv[i+1] );
+ i++;
+ Msg( "Vis Radius = %4.2f\n", g_VisRadius );
+ g_VisRadius = g_VisRadius * g_VisRadius; // so distance check can be squared
+ }
+ else if( !Q_stricmp( argv[i], "-trace" ) )
+ {
+ g_TraceClusterStart = atoi( argv[i+1] );
+ i++;
+ g_TraceClusterStop = atoi( argv[i+1] );
+ i++;
+ Msg( "Tracing vis from cluster %d to %d\n", g_TraceClusterStart, g_TraceClusterStop );
+ }
+ else if (!Q_stricmp (argv[i],"-nosort"))
+ {
+ Msg ("nosort = true\n");
+ nosort = true;
+ }
+ else if (!Q_stricmp (argv[i],"-tmpin"))
+ strcpy (inbase, "/tmp");
+ else if( !Q_stricmp( argv[i], "-low" ) )
+ {
+ g_bLowPriority = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
+ {
+ EnableFullMinidumps( true );
+ }
+ else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
+ {
+ }
+ else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
+ {
+ ++i;
+ }
+ else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
+ {
+ // nothing to do here, but don't bail on this option
+ }
+ // NOTE: the -mpi checks must come last here because they allow the previous argument
+ // to be -mpi as well. If it game before something else like -game, then if the previous
+ // argument was -mpi and the current argument was something valid like -game, it would skip it.
+ else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
+ {
+ if ( stricmp( argv[i], "-mpi" ) == 0 )
+ g_bUseMPI = true;
+
+ // Any other args that start with -mpi are ok too.
+ if ( i == argc - 1 )
+ break;
+ }
+ else if (argv[i][0] == '-')
+ {
+ Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
+ i = 100000; // force it to print the usage
+ break;
+ }
+ else
+ break;
+ }
+ return i;
+}
+
+
+void PrintCommandLine( int argc, char **argv )
+{
+ Warning( "Command line: " );
+ for ( int z=0; z < argc; z++ )
+ {
+ Warning( "\"%s\" ", argv[z] );
+ }
+ Warning( "\n\n" );
+}
+
+
+void PrintUsage( int argc, char **argv )
+{
+ PrintCommandLine( argc, argv );
+
+ Warning(
+ "usage : vvis [options...] bspfile\n"
+ "example: vvis -fast c:\\hl2\\hl2\\maps\\test\n"
+ "\n"
+ "Common options:\n"
+ "\n"
+ " -v (or -verbose): Turn on verbose output (also shows more command\n"
+ " -fast : Only do first quick pass on vis calculations.\n"
+ " -mpi : Use VMPI to distribute computations.\n"
+ " -low : Run as an idle-priority process.\n"
+ " env_fog_controller specifies one.\n"
+ "\n"
+ " -vproject <directory> : Override the VPROJECT environment variable.\n"
+ " -game <directory> : Same as -vproject.\n"
+ "\n"
+ "Other options:\n"
+ " -novconfig : Don't bring up graphical UI on vproject errors.\n"
+ " -radius_override: Force a vis radius, regardless of whether an\n"
+ " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
+ " -threads : Control the number of threads vbsp uses (defaults to the #\n"
+ " or processors on your machine).\n"
+ " -nosort : Don't sort portals (sorting is an optimization).\n"
+ " -tmpin : Make portals come from \\tmp\\<mapname>.\n"
+ " -tmpout : Make portals come from \\tmp\\<mapname>.\n"
+ " -trace <start cluster> <end cluster> : Writes a linefile that traces the vis from one cluster to another for debugging map vis.\n"
+ " -FullMinidumps : Write large minidumps on crash.\n"
+ " -x360 : Generate Xbox360 version of vsp\n"
+ " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
+ "\n"
+#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
+ );
+#else
+ " -mpi_ListParams : Show a list of VMPI parameters.\n"
+ "\n"
+ );
+
+ // Show VMPI parameters?
+ for ( int i=1; i < argc; i++ )
+ {
+ if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
+ {
+ Warning( "VMPI-specific options:\n\n" );
+
+ bool bIsSDKMode = VMPI_IsSDKMode();
+ for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
+ {
+ if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
+ continue;
+
+ Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
+ Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
+ Warning( "\n\n" );
+ }
+ break;
+ }
+ }
+#endif
+}
+
+
+int RunVVis( int argc, char **argv )
+{
+ char portalfile[1024];
+ char source[1024];
+ double start, end;
+
+
+ Msg( "Valve Software - vvis.exe (%s)\n", __DATE__ );
+
+ verbose = false;
+
+ Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) );
+ CmdLib_InitFileSystem( argv[ argc - 1 ] );
+
+ Q_FileBase( source, source, sizeof( source ) );
+
+ LoadCmdLineFromFile( argc, argv, source, "vvis" );
+ int i = ParseCommandLine( argc, argv );
+
+ // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
+ // so we prepend qdir here.
+ strcpy( source, ExpandPath( source ) );
+
+ if (i != argc - 1)
+ {
+ PrintUsage( argc, argv );
+ DeleteCmdLine( argc, argv );
+ CmdLib_Exit( 1 );
+ }
+
+ start = Plat_FloatTime();
+
+
+ if (!g_bUseMPI)
+ {
+ // Setup the logfile.
+ char logFile[512];
+ _snprintf( logFile, sizeof(logFile), "%s.log", source );
+ SetSpewFunctionLogFile( logFile );
+ }
+
+ // Run in the background?
+ if( g_bLowPriority )
+ {
+ SetLowPriority();
+ }
+
+ ThreadSetDefault ();
+
+ char targetPath[1024];
+ GetPlatformMapPath( source, targetPath, 0, 1024 );
+ Msg ("reading %s\n", targetPath);
+ LoadBSPFile (targetPath);
+ if (numnodes == 0 || numfaces == 0)
+ Error ("Empty map");
+ ParseEntities ();
+
+ // Check the VMF for a vis radius
+ if (!g_bUseRadius)
+ {
+ float flRadius = DetermineVisRadius( );
+ if (flRadius > 0.0f)
+ {
+ g_bUseRadius = true;
+ g_VisRadius = flRadius * flRadius;
+ }
+ }
+
+ if ( g_bUseRadius )
+ {
+ MarkLeavesAsRadial();
+ }
+
+ if ( inbase[0] == 0 )
+ {
+ strcpy( portalfile, source );
+ }
+ else
+ {
+ sprintf ( portalfile, "%s%s", inbase, argv[i] );
+ Q_StripExtension( portalfile, portalfile, sizeof( portalfile ) );
+ }
+ strcat (portalfile, ".prt");
+
+ Msg ("reading %s\n", portalfile);
+ LoadPortals (portalfile);
+
+ // don't write out results when simply doing a trace
+ if ( g_TraceClusterStart < 0 )
+ {
+ CalcVis ();
+ CalcPAS ();
+
+ // We need a mapping from cluster to leaves, since the PVS
+ // deals with clusters for both CalcVisibleFogVolumes and
+ BuildClusterTable();
+
+ CalcVisibleFogVolumes();
+ CalcDistanceFromLeavesToWater();
+
+ visdatasize = vismap_p - dvisdata;
+ Msg ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize*2);
+
+ Msg ("writing %s\n", targetPath);
+ WriteBSPFile (targetPath);
+ }
+ else
+ {
+ if ( g_TraceClusterStart < 0 || g_TraceClusterStart >= portalclusters || g_TraceClusterStop < 0 || g_TraceClusterStop >= portalclusters )
+ {
+ Error("Invalid cluster trace: %d to %d, valid range is 0 to %d\n", g_TraceClusterStart, g_TraceClusterStop, portalclusters-1 );
+ }
+ if ( g_bUseMPI )
+ {
+ Warning("Can't compile trace in MPI mode\n");
+ }
+ CalcVisTrace ();
+ WritePortalTrace(source);
+ }
+
+ end = Plat_FloatTime();
+
+ char str[512];
+ GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
+ Msg( "%s elapsed\n", str );
+
+ ReleasePakFileLumps();
+ DeleteCmdLine( argc, argv );
+ CmdLib_Cleanup();
+ return 0;
+}
+
+
+/*
+===========
+main
+===========
+*/
+int main (int argc, char **argv)
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f, false, false, false, false );
+ InstallAllocationFunctions();
+ InstallSpewFunction();
+
+ VVIS_SetupMPI( argc, argv );
+
+ // Install an exception handler.
+ if ( g_bUseMPI && !g_bMPIMaster )
+ SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
+ else
+ SetupDefaultToolsMinidumpHandler();
+
+ return RunVVis( argc, argv );
+}
+
+
+// When VVIS is used as a DLL (makes debugging vmpi vvis a lot easier), this is used to
+// get it going.
+class CVVisDLL : public ILaunchableDLL
+{
+public:
+ virtual int main( int argc, char **argv )
+ {
+ return ::main( argc, argv );
+ }
+};
+
+EXPOSE_SINGLE_INTERFACE( CVVisDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION );
diff --git a/mp/src/utils/vvis/vvis_dll-2010.vcxproj b/mp/src/utils/vvis/vvis_dll-2010.vcxproj new file mode 100644 index 00000000..3099cd82 --- /dev/null +++ b/mp/src/utils/vvis/vvis_dll-2010.vcxproj @@ -0,0 +1,291 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vvis_dll</ProjectName>
+ <ProjectGuid>{AC70A841-561F-4DAE-7864-E50541AD99ED}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vvis_dll</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vvis_dll</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi;..\vmpi\mysql\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=vvis_dll;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MPI;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vvis;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);odbc32.lib;odbccp32.lib;ws2_32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vvis_dll.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vvis_dll.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common;..\vmpi;..\vmpi\mysql\include</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;DLLNAME=vvis_dll;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;MPI;PROTECTED_THINGS_DISABLE;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vvis;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <GenerateXMLDocumentationFiles>false</GenerateXMLDocumentationFiles>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies);odbc32.lib;odbccp32.lib;ws2_32.lib</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vvis_dll.dll</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <AdditionalLibraryDirectories>..\..\lib\common;..\..\lib\public</AdditionalLibraryDirectories>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <LinkLibraryDependencies>false</LinkLibraryDependencies>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vvis_dll.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) "..\..\..\game\bin\$(TargetFileName)"
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib" />
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\tier2.lib" />
+ <Library Include="..\..\lib\public\vmpi.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h" />
+ <ClInclude Include="..\..\public\tier0\basetypes.h" />
+ <ClInclude Include="..\..\public\BSPFILE.H" />
+ <ClInclude Include="..\..\public\bspflags.h" />
+ <ClInclude Include="..\common\bsplib.h" />
+ <ClInclude Include="..\..\public\BSPTreeData.h" />
+ <ClInclude Include="..\..\public\mathlib\bumpvects.h" />
+ <ClInclude Include="..\..\public\tier1\byteswap.h" />
+ <ClInclude Include="..\..\public\tier1\checksum_crc.h" />
+ <ClInclude Include="..\..\public\tier1\checksum_md5.h" />
+ <ClInclude Include="..\common\cmdlib.h" />
+ <ClInclude Include="..\..\public\cmodel.h" />
+ <ClInclude Include="..\..\public\tier0\commonmacros.h" />
+ <ClInclude Include="..\..\public\GameBSPFile.h" />
+ <ClInclude Include="..\common\ISQLDBReplyTarget.h" />
+ <ClInclude Include="..\..\public\mathlib\mathlib.h" />
+ <ClInclude Include="mpivis.h" />
+ <ClInclude Include="..\common\MySqlDatabase.h" />
+ <ClInclude Include="..\common\pacifier.h" />
+ <ClInclude Include="..\common\scriplib.h" />
+ <ClInclude Include="..\..\public\tier1\strtools.h" />
+ <ClInclude Include="..\common\threads.h" />
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h" />
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h" />
+ <ClInclude Include="..\..\public\tier1\utlmemory.h" />
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h" />
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h" />
+ <ClInclude Include="..\..\public\tier1\utlvector.h" />
+ <ClInclude Include="..\..\public\vcollide.h" />
+ <ClInclude Include="..\..\public\mathlib\vector.h" />
+ <ClInclude Include="..\..\public\mathlib\vector2d.h" />
+ <ClInclude Include="vis.h" />
+ <ClInclude Include="..\vmpi\vmpi_distribute_work.h" />
+ <ClInclude Include="..\common\vmpi_tools_shared.h" />
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h" />
+ <ClInclude Include="..\..\public\wadtypes.h" />
+ <ClInclude Include="..\common\tools_minidump.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\bsplib.cpp" />
+ <ClCompile Include="..\common\cmdlib.cpp" />
+ <ClCompile Include="..\..\public\collisionutils.cpp" />
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp" />
+ <ClCompile Include="flow.cpp" />
+ <ClCompile Include="..\..\public\loadcmdline.cpp" />
+ <ClCompile Include="..\..\public\lumpfiles.cpp" />
+ <ClCompile Include="..\common\mpi_stats.cpp" />
+ <ClCompile Include="mpivis.cpp" />
+ <ClCompile Include="..\common\MySqlDatabase.cpp" />
+ <ClCompile Include="..\common\pacifier.cpp" />
+ <ClCompile Include="..\..\public\scratchpad3d.cpp" />
+ <ClCompile Include="..\common\scratchpad_helpers.cpp" />
+ <ClCompile Include="..\common\scriplib.cpp" />
+ <ClCompile Include="..\common\threads.cpp" />
+ <ClCompile Include="..\common\tools_minidump.cpp" />
+ <ClCompile Include="..\common\vmpi_tools_shared.cpp" />
+ <ClCompile Include="vvis.cpp" />
+ <ClCompile Include="WaterDist.cpp" />
+ <ClCompile Include="..\..\public\zip_utils.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vvis/vvis_dll-2010.vcxproj.filters b/mp/src/utils/vvis/vvis_dll-2010.vcxproj.filters new file mode 100644 index 00000000..d25742b8 --- /dev/null +++ b/mp/src/utils/vvis/vvis_dll-2010.vcxproj.filters @@ -0,0 +1,218 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\mathlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier2.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vmpi.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\mathlib\amd3dx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\basetypes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\BSPFILE.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\bspflags.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\bsplib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\BSPTreeData.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\bumpvects.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\byteswap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\checksum_crc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\checksum_md5.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\cmdlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\cmodel.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier0\commonmacros.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\GameBSPFile.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\ISQLDBReplyTarget.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\mathlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="mpivis.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\MySqlDatabase.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\pacifier.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\scriplib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\strtools.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\threads.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlbuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utllinkedlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlmemory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlrbtree.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlsymbol.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\tier1\utlvector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vcollide.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\mathlib\vector2d.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="vis.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\vmpi\vmpi_distribute_work.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\vmpi_tools_shared.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\vstdlib\vstdlib.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\public\wadtypes.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\common\tools_minidump.h">
+ <Filter>Source Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\common\bsplib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\cmdlib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\collisionutils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\filesystem_helpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="flow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\loadcmdline.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\lumpfiles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\mpi_stats.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="mpivis.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\MySqlDatabase.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\pacifier.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\scratchpad3d.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\scratchpad_helpers.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\scriplib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\threads.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\tools_minidump.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\common\vmpi_tools_shared.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vvis.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WaterDist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\public\zip_utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vvis_launcher/StdAfx.cpp b/mp/src/utils/vvis_launcher/StdAfx.cpp new file mode 100644 index 00000000..4165f58f --- /dev/null +++ b/mp/src/utils/vvis_launcher/StdAfx.cpp @@ -0,0 +1,15 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.cpp : source file that includes just the standard includes
+// vvis_launcher.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/mp/src/utils/vvis_launcher/StdAfx.h b/mp/src/utils/vvis_launcher/StdAfx.h new file mode 100644 index 00000000..8446846d --- /dev/null +++ b/mp/src/utils/vvis_launcher/StdAfx.h @@ -0,0 +1,31 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__29316173_1244_4B6A_B361_1ADB126E69F2__INCLUDED_)
+#define AFX_STDAFX_H__29316173_1244_4B6A_B361_1ADB126E69F2__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include <windows.h>
+#include <stdio.h>
+#include "interface.h"
+
+// TODO: reference additional headers your program requires here
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__29316173_1244_4B6A_B361_1ADB126E69F2__INCLUDED_)
diff --git a/mp/src/utils/vvis_launcher/vvis_launcher-2010.vcxproj b/mp/src/utils/vvis_launcher/vvis_launcher-2010.vcxproj new file mode 100644 index 00000000..62aee797 --- /dev/null +++ b/mp/src/utils/vvis_launcher/vvis_launcher-2010.vcxproj @@ -0,0 +1,248 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>Vvis_launcher</ProjectName>
+ <ProjectGuid>{E3E2CF1C-9EE4-3173-C39F-D0D4F5483CB6}</ProjectGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vvis</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
+ <TargetName>vvis</TargetName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\Debug\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</PostBuildEventUseInBuild>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\Release\win32\</IntDir>
+ <ExecutablePath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">..\..\devtools\vstools;$(ExecutablePath);$(Path)</ExecutablePath>
+ <PreBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreBuildEventUseInBuild>
+ <PreLinkEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PreLinkEventUseInBuild>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <GenerateManifest Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</GenerateManifest>
+ <PostBuildEventUseInBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</PostBuildEventUseInBuild>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vvis_launcher.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP</AdditionalOptions>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_HAS_ITERATOR_DEBUGGING=0;WIN32;_WIN32;_DEBUG;DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vvis_launcher;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>Debug/vvis_launcher.pch</PrecompiledHeaderOutputFile>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vvis.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmt</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vvis.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <PreBuildEvent>
+ <Command>if EXIST ..\..\..\game\bin\$(TargetFileName) for /f "delims=" %%A in ('attrib "..\..\..\game\bin\$(TargetFileName)"') do set valveTmpIsReadOnly="%%A"
set valveTmpIsReadOnlyLetter=%valveTmpIsReadOnly:~6,1%
if "%valveTmpIsReadOnlyLetter%"=="R" del /q "$(TargetDir)"$(TargetFileName)
if exist ..\..\devtools\bin\vpc.exe ..\..\devtools\bin\vpc.exe -crc2 vvis_launcher.vcxproj
if ERRORLEVEL 1 exit 1</Command>
+ </PreBuildEvent>
+ <ClCompile>
+ <AdditionalOptions> /MP /d2Zi+</AdditionalOptions>
+ <Optimization>MaxSpeed</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\common;..\..\public;..\..\public\tier0;..\..\public\tier1;..\common</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;RAD_TELEMETRY_DISABLED;COMPILER_MSVC32;VPCGAMECAPS=VALVE;PROJECTDIR=D:\dev\games\rel\hl2\src\utils\vvis_launcher;_DLL_EXT=.dll;VPCGAME=valve</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>false</ExceptionHandling>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <EnableEnhancedInstructionSet>StreamingSIMDExtensions</EnableEnhancedInstructionSet>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <OpenMPSupport>false</OpenMPSupport>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>Debug/vvis_launcher.pch</PrecompiledHeaderOutputFile>
+ <ExpandAttributedSource>false</ExpandAttributedSource>
+ <AssemblerOutput>NoListing</AssemblerOutput>
+ <AssemblerListingLocation>$(IntDir)/</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)/</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)/</ProgramDataBaseFileName>
+ <BrowseInformation>false</BrowseInformation>
+ <WarningLevel>Level4</WarningLevel>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>CompileAsCpp</CompileAs>
+ <BrowseInformationFile>$(IntDir)/</BrowseInformationFile>
+ <ErrorReporting>Prompt</ErrorReporting>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE</PreprocessorDefinitions>
+ <Culture>1033</Culture>
+ </ResourceCompile>
+ <PreLinkEvent>
+ </PreLinkEvent>
+ <Link>
+ <AdditionalOptions> /DYNAMICBASE /NXCOMPAT /ignore:4221</AdditionalOptions>
+ <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
+ <ShowProgress>NotSet</ShowProgress>
+ <OutputFile>$(OutDir)\vvis.exe</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreSpecificDefaultLibraries>libc;libcd;libcmtd</IgnoreSpecificDefaultLibraries>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)/$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>$(IntDir)/$(TargetName).map</MapFileName>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <BaseAddress> </BaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <LinkErrorReporting>PromptImmediately</LinkErrorReporting>
+ </Link>
+ <Manifest>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Manifest>
+ <Xdcmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Xdcmake>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>$(OutDir)/vvis.bsc</OutputFile>
+ </Bscmake>
+ <PostBuildEvent>
+ <Message>Publishing to ..\..\..\game\bin</Message>
+ <Command>if not exist "..\..\..\game\bin" mkdir "..\..\..\game\bin"
copy "$(TargetDir)"$(TargetFileName) ..\..\..\game\bin\$(TargetFileName)
if ERRORLEVEL 1 goto BuildEventFailed
if exist "$(TargetDir)"$(TargetName).map copy "$(TargetDir)"$(TargetName).map ..\..\..\game\bin\$(TargetName).map
copy "$(TargetDir)"$(TargetName).pdb ..\..\..\game\bin\$(TargetName).pdb
if ERRORLEVEL 1 goto BuildEventFailed
goto BuildEventOK
:BuildEventFailed
echo *** ERROR! PostBuildStep FAILED for $(ProjectName)! EXE or DLL is probably running. ***
del /q "$(TargetDir)"$(TargetFileName)
exit 1
:BuildEventOK
</Command>
+ </PostBuildEvent>
+ <CustomBuildStep>
+ </CustomBuildStep>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\tier0.lib" />
+ <Library Include="..\..\lib\public\tier1.lib" />
+ <Library Include="..\..\lib\public\vstdlib.lib" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier1\interface.h" />
+ <ClInclude Include="StdAfx.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="StdAfx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="vvis_launcher.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Compiling pointeroverride.asm</Message>
+ <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(VCInstallDir)bin\ml.exe" /c /Cp /Zi /Fo"$(IntDir)\%(Filename).obj" "%(FullPath)"</Command>
+ <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\%(Filename).obj</Outputs>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/mp/src/utils/vvis_launcher/vvis_launcher-2010.vcxproj.filters b/mp/src/utils/vvis_launcher/vvis_launcher-2010.vcxproj.filters new file mode 100644 index 00000000..c742afd3 --- /dev/null +++ b/mp/src/utils/vvis_launcher/vvis_launcher-2010.vcxproj.filters @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{1680C80B-FF1E-EA4D-9817-CC12254F2E40}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Link Libraries">
+ <UniqueIdentifier>{C5D73B3A-C648-896C-B7CE-F174808E5BA5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{BA03E055-4FA2-FCE3-8A1C-D348547D379C}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Library Include="..\..\lib\public\tier0.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\tier1.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ <Library Include="..\..\lib\public\vstdlib.lib">
+ <Filter>Link Libraries</Filter>
+ </Library>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\public\tier1\interface.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="StdAfx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\public\tier0\memoverride.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="StdAfx.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="vvis_launcher.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\..\public\tier0\pointeroverride.asm">
+ <Filter>Source Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ </ItemGroup>
+</Project>
diff --git a/mp/src/utils/vvis_launcher/vvis_launcher.cpp b/mp/src/utils/vvis_launcher/vvis_launcher.cpp new file mode 100644 index 00000000..6d628040 --- /dev/null +++ b/mp/src/utils/vvis_launcher/vvis_launcher.cpp @@ -0,0 +1,79 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vvis_launcher.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include <direct.h>
+#include "tier1/strtools.h"
+#include "tier0/icommandline.h"
+#include "ilaunchabledll.h"
+
+
+
+char* GetLastErrorString()
+{
+ static char err[2048];
+
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
+ LocalFree( lpMsgBuf );
+
+ err[ sizeof( err ) - 1 ] = 0;
+
+ return err;
+}
+
+
+int main(int argc, char* argv[])
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+ const char *pDLLName = "vvis_dll.dll";
+
+ CSysModule *pModule = Sys_LoadModule( pDLLName );
+ if ( !pModule )
+ {
+ printf( "vvis launcher error: can't load %s\n%s", pDLLName, GetLastErrorString() );
+ return 1;
+ }
+
+ CreateInterfaceFn fn = Sys_GetFactory( pModule );
+ if( !fn )
+ {
+ printf( "vvis launcher error: can't get factory from %s\n", pDLLName );
+ Sys_UnloadModule( pModule );
+ return 2;
+ }
+
+ int retCode = 0;
+ ILaunchableDLL *pDLL = (ILaunchableDLL*)fn( LAUNCHABLE_DLL_INTERFACE_VERSION, &retCode );
+ if( !pDLL )
+ {
+ printf( "vvis launcher error: can't get IVVisDLL interface from %s\n", pDLLName );
+ Sys_UnloadModule( pModule );
+ return 3;
+ }
+
+ pDLL->main( argc, argv );
+ Sys_UnloadModule( pModule );
+
+ return 0;
+}
+
|