Initial commit.

This commit is contained in:
Mikko Mononen 2009-03-29 10:30:52 +00:00
parent fc115e3dc6
commit 06832c885e
41 changed files with 31349 additions and 0 deletions

View File

@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual C++ Express 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Recast", "Recast.vcproj", "{CEF242C5-E9A3-403B-BAFF-99397BDA5730}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CEF242C5-E9A3-403B-BAFF-99397BDA5730}.Debug|Win32.ActiveCfg = Debug|Win32
{CEF242C5-E9A3-403B-BAFF-99397BDA5730}.Debug|Win32.Build.0 = Debug|Win32
{CEF242C5-E9A3-403B-BAFF-99397BDA5730}.Release|Win32.ActiveCfg = Release|Win32
{CEF242C5-E9A3-403B-BAFF-99397BDA5730}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

BIN
Recast/Build/VC9/Recast.suo Normal file

Binary file not shown.

View File

@ -0,0 +1,275 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="Recast"
ProjectGUID="{CEF242C5-E9A3-403B-BAFF-99397BDA5730}"
RootNamespace="Recast"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="..\..\Examples"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\Contrib\SDL\include;..\..\Include;..\..\Examples;$(NOINHERIT)"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="opengl32.lib glu32.lib sdlmain.lib sdl.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="..\..\Contrib\SDL\lib"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="..\..\Examples"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="..\..\Contrib\SDL\include;..\..\Include;..\..\Examples;$(NOINHERIT)"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="opengl32.lib glu32.lib sdlmain.lib sdl.lib"
LinkIncremental="1"
AdditionalLibraryDirectories="..\..\Contrib\SDL\lib"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\Examples\demo.cpp"
>
</File>
<File
RelativePath="..\..\Examples\glfont.cpp"
>
</File>
<File
RelativePath="..\..\Examples\imgui.cpp"
>
</File>
<File
RelativePath="..\..\Examples\MeshLoaderObj.cpp"
>
</File>
<File
RelativePath="..\..\Source\Recast.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastContour.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastDebugDraw.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastFilter.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastLog.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastMesh.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastRasterization.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastRegion.cpp"
>
</File>
<File
RelativePath="..\..\Source\RecastTimer.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\Examples\glfont.h"
>
</File>
<File
RelativePath="..\..\Examples\imgui.h"
>
</File>
<File
RelativePath="..\..\Examples\MeshLoaderObj.h"
>
</File>
<File
RelativePath="..\..\Include\Recast.h"
>
</File>
<File
RelativePath="..\..\Include\RecastDebugDraw.h"
>
</File>
<File
RelativePath="..\..\Include\RecastLog.h"
>
</File>
<File
RelativePath="..\..\Include\RecastTimer.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.yourcompany.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

Binary file not shown.

View File

@ -0,0 +1,250 @@
// !$*UTF8*$!
{
29B97313FDCFA39411CA2CEA /* Project object */ = {
activeBuildConfigurationName = Debug;
activeExecutable = 6B8632970F78114600E2684A /* Recast */;
activeTarget = 8D1107260486CEB800E47090 /* Recast */;
addToTargets = (
8D1107260486CEB800E47090 /* Recast */,
);
codeSenseManager = 6B8632AA0F78115100E2684A /* Code sense */;
executables = (
6B8632970F78114600E2684A /* Recast */,
);
perUserDictionary = {
PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = {
PBXFileTableDataSourceColumnSortingDirectionKey = "-1";
PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID;
PBXFileTableDataSourceColumnWidthsKey = (
20,
625,
20,
48,
43,
43,
20,
);
PBXFileTableDataSourceColumnsKey = (
PBXFileDataSource_FiletypeID,
PBXFileDataSource_Filename_ColumnID,
PBXFileDataSource_Built_ColumnID,
PBXFileDataSource_ObjectSize_ColumnID,
PBXFileDataSource_Errors_ColumnID,
PBXFileDataSource_Warnings_ColumnID,
PBXFileDataSource_Target_ColumnID,
);
};
PBXPerProjectTemplateStateSaveDate = 259608223;
PBXWorkspaceStateSaveDate = 259608223;
};
perUserProjectItems = {
6B68D7640F781974007E6D78 /* PBXTextBookmark */ = 6B68D7640F781974007E6D78 /* PBXTextBookmark */;
6B85EA8A0F7970D500780B56 /* PBXTextBookmark */ = 6B85EA8A0F7970D500780B56 /* PBXTextBookmark */;
6B8633370F7813A600E2684A /* PBXTextBookmark */ = 6B8633370F7813A600E2684A /* PBXTextBookmark */;
6B86333A0F7813A600E2684A /* PBXTextBookmark */ = 6B86333A0F7813A600E2684A /* PBXTextBookmark */;
6B86333B0F7813A600E2684A /* PBXTextBookmark */ = 6B86333B0F7813A600E2684A /* PBXTextBookmark */;
6B8633780F78173000E2684A /* PBXTextBookmark */ = 6B8633780F78173000E2684A /* PBXTextBookmark */;
6B8633790F78173000E2684A /* PBXTextBookmark */ = 6B8633790F78173000E2684A /* PBXTextBookmark */;
6B86337C0F78173000E2684A /* PBXTextBookmark */ = 6B86337C0F78173000E2684A /* PBXTextBookmark */;
6B86337E0F78173000E2684A /* PBXTextBookmark */ = 6B86337E0F78173000E2684A /* PBXTextBookmark */;
6B8633820F78174400E2684A /* PBXTextBookmark */ = 6B8633820F78174400E2684A /* PBXTextBookmark */;
6B8633840F78174400E2684A /* PBXTextBookmark */ = 6B8633840F78174400E2684A /* PBXTextBookmark */;
};
sourceControlManager = 6B8632A90F78115100E2684A /* Source Control */;
userBuildSettings = {
};
};
6B68D7640F781974007E6D78 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8632BC0F7811CB00E2684A /* demo.cpp */;
name = "demo.cpp: 631";
rLen = 0;
rLoc = 16040;
rType = 0;
vrLen = 1544;
vrLoc = 15684;
};
6B85EA8A0F7970D500780B56 /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8632BC0F7811CB00E2684A /* demo.cpp */;
name = "demo.cpp: 640";
rLen = 0;
rLoc = 16397;
rType = 0;
vrLen = 1544;
vrLoc = 15684;
};
6B8632970F78114600E2684A /* Recast */ = {
isa = PBXExecutable;
activeArgIndices = (
);
argumentStrings = (
);
autoAttachOnCrash = 1;
breakpointsEnabled = 0;
configStateDict = {
};
customDataFormattersEnabled = 1;
debuggerPlugin = GDBDebugging;
disassemblyDisplayState = 0;
dylibVariantSuffix = "";
enableDebugStr = 1;
environmentEntries = (
);
executableSystemSymbolLevel = 0;
executableUserSymbolLevel = 0;
libgmallocEnabled = 0;
name = Recast;
sourceDirectories = (
);
};
6B8632A90F78115100E2684A /* Source Control */ = {
isa = PBXSourceControlManager;
fallbackIsa = XCSourceControlManager;
isSCMEnabled = 0;
scmConfiguration = {
};
};
6B8632AA0F78115100E2684A /* Code sense */ = {
isa = PBXCodeSenseManager;
indexTemplatePath = "";
};
6B8632BA0F78119A00E2684A /* SDLMain.m */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {943, 6016}}";
sepNavSelRange = "{4217, 0}";
sepNavVisRange = "{3795, 1449}";
};
};
6B8632BC0F7811CB00E2684A /* demo.cpp */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {803, 11568}}";
sepNavSelRange = "{16397, 0}";
sepNavVisRange = "{15684, 1544}";
};
};
6B8633370F7813A600E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8633380F7813A600E2684A /* SDL_events.h */;
name = "SDL_events.h: 238";
rLen = 0;
rLoc = 8641;
rType = 0;
vrLen = 785;
vrLoc = 8262;
};
6B8633380F7813A600E2684A /* SDL_events.h */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
name = SDL_events.h;
path = /Library/Frameworks/SDL.framework/Versions/A/Headers/SDL_events.h;
sourceTree = "<absolute>";
};
6B86333A0F7813A600E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8632BC0F7811CB00E2684A /* demo.cpp */;
name = "demo.cpp: 37";
rLen = 0;
rLoc = 0;
rType = 0;
vrLen = 438;
vrLoc = 598;
};
6B86333B0F7813A600E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B86333C0F7813A600E2684A /* SDL_events.h */;
name = "SDL_events.h: 238";
rLen = 0;
rLoc = 8641;
rType = 0;
vrLen = 785;
vrLoc = 8262;
};
6B86333C0F7813A600E2684A /* SDL_events.h */ = {
isa = PBXFileReference;
lastKnownFileType = sourcecode.c.h;
name = SDL_events.h;
path = /Library/Frameworks/SDL.framework/Versions/A/Headers/SDL_events.h;
sourceTree = "<absolute>";
};
6B86335E0F7816FA00E2684A /* Recast.cpp */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {803, 4720}}";
sepNavSelRange = "{827, 0}";
sepNavVisRange = "{441, 1040}";
};
};
6B8633650F7816FA00E2684A /* RecastMesh.cpp */ = {
uiCtxt = {
sepNavIntBoundsRect = "{{0, 0}, {803, 10768}}";
sepNavSelRange = "{1485, 0}";
sepNavVisRange = "{667, 687}";
};
};
6B8633780F78173000E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8632BA0F78119A00E2684A /* SDLMain.m */;
name = "SDLMain.m: 146";
rLen = 0;
rLoc = 4217;
rType = 0;
vrLen = 1449;
vrLoc = 3795;
};
6B8633790F78173000E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B86335E0F7816FA00E2684A /* Recast.cpp */;
name = "Recast.cpp: 15";
rLen = 0;
rLoc = 827;
rType = 0;
vrLen = 1040;
vrLoc = 441;
};
6B86337C0F78173000E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8632BA0F78119A00E2684A /* SDLMain.m */;
name = "SDLMain.m: 146";
rLen = 0;
rLoc = 4217;
rType = 0;
vrLen = 1449;
vrLoc = 3795;
};
6B86337E0F78173000E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B86335E0F7816FA00E2684A /* Recast.cpp */;
name = "Recast.cpp: 15";
rLen = 0;
rLoc = 827;
rType = 0;
vrLen = 1040;
vrLoc = 441;
};
6B8633820F78174400E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8633650F7816FA00E2684A /* RecastMesh.cpp */;
name = "RecastMesh.cpp: 42";
rLen = 0;
rLoc = 1485;
rType = 0;
vrLen = 687;
vrLoc = 667;
};
6B8633840F78174400E2684A /* PBXTextBookmark */ = {
isa = PBXTextBookmark;
fRef = 6B8633650F7816FA00E2684A /* RecastMesh.cpp */;
name = "RecastMesh.cpp: 42";
rLen = 0;
rLoc = 1485;
rType = 0;
vrLen = 687;
vrLoc = 667;
};
8D1107260486CEB800E47090 /* Recast */ = {
activeExec = 0;
executables = (
6B8632970F78114600E2684A /* Recast */,
);
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,364 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 45;
objects = {
/* Begin PBXBuildFile section */
1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
6B8632BB0F78119A00E2684A /* SDLMain.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B8632BA0F78119A00E2684A /* SDLMain.m */; };
6B8632BD0F7811CB00E2684A /* demo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8632BC0F7811CB00E2684A /* demo.cpp */; };
6B8632DA0F78122C00E2684A /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B8632D90F78122C00E2684A /* SDL.framework */; };
6B8632DC0F78123E00E2684A /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B8632DB0F78123E00E2684A /* OpenGL.framework */; };
6B86335A0F7816C900E2684A /* glfont.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633540F7816C900E2684A /* glfont.cpp */; };
6B86335B0F7816C900E2684A /* imgui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633560F7816C900E2684A /* imgui.cpp */; };
6B86335C0F7816C900E2684A /* MeshLoaderObj.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633580F7816C900E2684A /* MeshLoaderObj.cpp */; };
6B86336A0F7816FA00E2684A /* Recast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B86335E0F7816FA00E2684A /* Recast.cpp */; };
6B86336B0F7816FA00E2684A /* RecastContour.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B86335F0F7816FA00E2684A /* RecastContour.cpp */; };
6B86336C0F7816FA00E2684A /* RecastDebugDraw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633600F7816FA00E2684A /* RecastDebugDraw.cpp */; };
6B86336D0F7816FA00E2684A /* RecastFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633620F7816FA00E2684A /* RecastFilter.cpp */; };
6B86336E0F7816FA00E2684A /* RecastLog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633630F7816FA00E2684A /* RecastLog.cpp */; };
6B86336F0F7816FA00E2684A /* RecastMesh.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633650F7816FA00E2684A /* RecastMesh.cpp */; };
6B8633700F7816FA00E2684A /* RecastRasterization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633660F7816FA00E2684A /* RecastRasterization.cpp */; };
6B8633710F7816FA00E2684A /* RecastRegion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633670F7816FA00E2684A /* RecastRegion.cpp */; };
6B8633720F7816FA00E2684A /* RecastTimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6B8633680F7816FA00E2684A /* RecastTimer.cpp */; };
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = "<group>"; };
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
32CA4F630368D1EE00C91783 /* Recast_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Recast_Prefix.pch; sourceTree = "<group>"; };
6B68D7620F78196F007E6D78 /* RecastDebugDraw.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecastDebugDraw.h; path = ../../Include/RecastDebugDraw.h; sourceTree = SOURCE_ROOT; };
6B68D7630F78196F007E6D78 /* RecastTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecastTimer.h; path = ../../Include/RecastTimer.h; sourceTree = SOURCE_ROOT; };
6B8632B90F78119A00E2684A /* SDLMain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDLMain.h; path = ../../Examples/SDLMain.h; sourceTree = SOURCE_ROOT; };
6B8632BA0F78119A00E2684A /* SDLMain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLMain.m; path = ../../Examples/SDLMain.m; sourceTree = SOURCE_ROOT; };
6B8632BC0F7811CB00E2684A /* demo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = demo.cpp; path = ../../Examples/demo.cpp; sourceTree = SOURCE_ROOT; };
6B8632D90F78122C00E2684A /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = Library/Frameworks/SDL.framework; sourceTree = SDKROOT; };
6B8632DB0F78123E00E2684A /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
6B8633540F7816C900E2684A /* glfont.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = glfont.cpp; path = ../../Examples/glfont.cpp; sourceTree = SOURCE_ROOT; };
6B8633550F7816C900E2684A /* glfont.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = glfont.h; path = ../../Examples/glfont.h; sourceTree = SOURCE_ROOT; };
6B8633560F7816C900E2684A /* imgui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui.cpp; path = ../../Examples/imgui.cpp; sourceTree = SOURCE_ROOT; };
6B8633570F7816C900E2684A /* imgui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui.h; path = ../../Examples/imgui.h; sourceTree = SOURCE_ROOT; };
6B8633580F7816C900E2684A /* MeshLoaderObj.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MeshLoaderObj.cpp; path = ../../Examples/MeshLoaderObj.cpp; sourceTree = SOURCE_ROOT; };
6B8633590F7816C900E2684A /* MeshLoaderObj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MeshLoaderObj.h; path = ../../Examples/MeshLoaderObj.h; sourceTree = SOURCE_ROOT; };
6B86335D0F7816ED00E2684A /* Recast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Recast.h; path = ../../Include/Recast.h; sourceTree = SOURCE_ROOT; };
6B86335E0F7816FA00E2684A /* Recast.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Recast.cpp; path = ../../Source/Recast.cpp; sourceTree = SOURCE_ROOT; };
6B86335F0F7816FA00E2684A /* RecastContour.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastContour.cpp; path = ../../Source/RecastContour.cpp; sourceTree = SOURCE_ROOT; };
6B8633600F7816FA00E2684A /* RecastDebugDraw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastDebugDraw.cpp; path = ../../Source/RecastDebugDraw.cpp; sourceTree = SOURCE_ROOT; };
6B8633620F7816FA00E2684A /* RecastFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastFilter.cpp; path = ../../Source/RecastFilter.cpp; sourceTree = SOURCE_ROOT; };
6B8633630F7816FA00E2684A /* RecastLog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastLog.cpp; path = ../../Source/RecastLog.cpp; sourceTree = SOURCE_ROOT; };
6B8633640F7816FA00E2684A /* RecastLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RecastLog.h; path = ../../Source/RecastLog.h; sourceTree = SOURCE_ROOT; };
6B8633650F7816FA00E2684A /* RecastMesh.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastMesh.cpp; path = ../../Source/RecastMesh.cpp; sourceTree = SOURCE_ROOT; };
6B8633660F7816FA00E2684A /* RecastRasterization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastRasterization.cpp; path = ../../Source/RecastRasterization.cpp; sourceTree = SOURCE_ROOT; };
6B8633670F7816FA00E2684A /* RecastRegion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastRegion.cpp; path = ../../Source/RecastRegion.cpp; sourceTree = SOURCE_ROOT; };
6B8633680F7816FA00E2684A /* RecastTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RecastTimer.cpp; path = ../../Source/RecastTimer.cpp; sourceTree = SOURCE_ROOT; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8D1107320486CEB800E47090 /* Recast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Recast.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8D11072E0486CEB800E47090 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
6B8632DA0F78122C00E2684A /* SDL.framework in Frameworks */,
6B8632DC0F78123E00E2684A /* OpenGL.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
080E96DDFE201D6D7F000001 /* Classes */ = {
isa = PBXGroup;
children = (
6B8633730F7816FE00E2684A /* Recast */,
6B8633540F7816C900E2684A /* glfont.cpp */,
6B8633550F7816C900E2684A /* glfont.h */,
6B8633560F7816C900E2684A /* imgui.cpp */,
6B8633570F7816C900E2684A /* imgui.h */,
6B8633580F7816C900E2684A /* MeshLoaderObj.cpp */,
6B8633590F7816C900E2684A /* MeshLoaderObj.h */,
6B8632B90F78119A00E2684A /* SDLMain.h */,
6B8632BA0F78119A00E2684A /* SDLMain.m */,
6B8632BC0F7811CB00E2684A /* demo.cpp */,
);
name = Classes;
sourceTree = "<group>";
};
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
isa = PBXGroup;
children = (
6B8632D90F78122C00E2684A /* SDL.framework */,
6B8632DB0F78123E00E2684A /* OpenGL.framework */,
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
);
name = "Linked Frameworks";
sourceTree = "<group>";
};
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
19C28FACFE9D520D11CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
8D1107320486CEB800E47090 /* Recast.app */,
);
name = Products;
sourceTree = "<group>";
};
29B97314FDCFA39411CA2CEA /* Recast */ = {
isa = PBXGroup;
children = (
080E96DDFE201D6D7F000001 /* Classes */,
29B97315FDCFA39411CA2CEA /* Other Sources */,
29B97317FDCFA39411CA2CEA /* Resources */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
);
name = Recast;
sourceTree = "<group>";
};
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
isa = PBXGroup;
children = (
32CA4F630368D1EE00C91783 /* Recast_Prefix.pch */,
);
name = "Other Sources";
sourceTree = "<group>";
};
29B97317FDCFA39411CA2CEA /* Resources */ = {
isa = PBXGroup;
children = (
8D1107310486CEB800E47090 /* Info.plist */,
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
1DDD58140DA1D0A300B32029 /* MainMenu.xib */,
);
name = Resources;
sourceTree = "<group>";
};
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */,
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
6B8633730F7816FE00E2684A /* Recast */ = {
isa = PBXGroup;
children = (
6B68D7620F78196F007E6D78 /* RecastDebugDraw.h */,
6B68D7630F78196F007E6D78 /* RecastTimer.h */,
6B86335E0F7816FA00E2684A /* Recast.cpp */,
6B86335F0F7816FA00E2684A /* RecastContour.cpp */,
6B8633600F7816FA00E2684A /* RecastDebugDraw.cpp */,
6B8633620F7816FA00E2684A /* RecastFilter.cpp */,
6B8633630F7816FA00E2684A /* RecastLog.cpp */,
6B8633640F7816FA00E2684A /* RecastLog.h */,
6B8633650F7816FA00E2684A /* RecastMesh.cpp */,
6B8633660F7816FA00E2684A /* RecastRasterization.cpp */,
6B8633670F7816FA00E2684A /* RecastRegion.cpp */,
6B8633680F7816FA00E2684A /* RecastTimer.cpp */,
6B86335D0F7816ED00E2684A /* Recast.h */,
);
name = Recast;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
8D1107260486CEB800E47090 /* Recast */ = {
isa = PBXNativeTarget;
buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Recast" */;
buildPhases = (
8D1107290486CEB800E47090 /* Resources */,
8D11072C0486CEB800E47090 /* Sources */,
8D11072E0486CEB800E47090 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Recast;
productInstallPath = "$(HOME)/Applications";
productName = Recast;
productReference = 8D1107320486CEB800E47090 /* Recast.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Recast" */;
compatibilityVersion = "Xcode 3.1";
hasScannedForEncodings = 1;
mainGroup = 29B97314FDCFA39411CA2CEA /* Recast */;
projectDirPath = "";
projectRoot = "";
targets = (
8D1107260486CEB800E47090 /* Recast */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
8D1107290486CEB800E47090 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8D11072C0486CEB800E47090 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6B8632BB0F78119A00E2684A /* SDLMain.m in Sources */,
6B8632BD0F7811CB00E2684A /* demo.cpp in Sources */,
6B86335A0F7816C900E2684A /* glfont.cpp in Sources */,
6B86335B0F7816C900E2684A /* imgui.cpp in Sources */,
6B86335C0F7816C900E2684A /* MeshLoaderObj.cpp in Sources */,
6B86336A0F7816FA00E2684A /* Recast.cpp in Sources */,
6B86336B0F7816FA00E2684A /* RecastContour.cpp in Sources */,
6B86336C0F7816FA00E2684A /* RecastDebugDraw.cpp in Sources */,
6B86336D0F7816FA00E2684A /* RecastFilter.cpp in Sources */,
6B86336E0F7816FA00E2684A /* RecastLog.cpp in Sources */,
6B86336F0F7816FA00E2684A /* RecastMesh.cpp in Sources */,
6B8633700F7816FA00E2684A /* RecastRasterization.cpp in Sources */,
6B8633710F7816FA00E2684A /* RecastRegion.cpp in Sources */,
6B8633720F7816FA00E2684A /* RecastTimer.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
089C165DFE840E0CC02AAC07 /* English */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
1DDD58150DA1D0A300B32029 /* English */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CONFIGURATION_BUILD_DIR = ../../Examples;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Recast_Prefix.pch;
HEADER_SEARCH_PATHS = "/Library/Frameworks/SDL.framework/Headers/**";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = Recast;
};
name = Debug;
};
C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CONFIGURATION_BUILD_DIR = ../../Examples;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Recast_Prefix.pch;
HEADER_SEARCH_PATHS = "/Library/Frameworks/SDL.framework/Headers/**";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(HOME)/Applications";
PRODUCT_NAME = Recast;
};
name = Release;
};
C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
PREBINDING = NO;
SDKROOT = macosx10.5;
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
SDKROOT = macosx10.5;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Recast" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C01FCF4B08A954540054247B /* Debug */,
C01FCF4C08A954540054247B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Recast" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C01FCF4F08A954540054247B /* Debug */,
C01FCF5008A954540054247B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
}

View File

@ -0,0 +1,7 @@
//
// Prefix header for all source files of the 'Recast' target in the 'Recast' project
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif

View File

@ -0,0 +1 @@
Download SDL Developer Libraries from http://www.libsdl.org and unzip them here.

View File

@ -0,0 +1,219 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include "MeshLoaderObj.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _USE_MATH_DEFINES
#include <math.h>
rcMeshLoaderObj::rcMeshLoaderObj() :
m_verts(0),
m_normals(0),
m_tris(0),
m_vertCount(0),
m_triCount(0)
{
}
rcMeshLoaderObj::~rcMeshLoaderObj()
{
delete [] m_verts;
delete [] m_normals;
delete [] m_tris;
}
void rcMeshLoaderObj::addVertex(float x, float y, float z, int& cap)
{
if (m_vertCount+1 > cap)
{
cap = !cap ? 8 : cap*2;
float* nv = new float[cap*3];
if (m_vertCount)
memcpy(nv, m_verts, m_vertCount*3*sizeof(float));
delete [] m_verts;
m_verts = nv;
}
float* dst = &m_verts[m_vertCount*3];
*dst++ = x;
*dst++ = y;
*dst++ = z;
m_vertCount++;
}
void rcMeshLoaderObj::addTriangle(int a, int b, int c, int& cap)
{
if (m_triCount+1 > cap)
{
cap = !cap ? 8 : cap*2;
int* nv = new int[cap*3];
if (m_triCount)
memcpy(nv, m_tris, m_triCount*3*sizeof(int));
delete [] m_tris;
m_tris = nv;
}
int* dst = &m_tris[m_triCount*3];
*dst++ = a;
*dst++ = b;
*dst++ = c;
m_triCount++;
}
static char* parseRow(char* buf, char* bufEnd, char* row, int len)
{
bool cont = false;
bool start = true;
bool done = false;
int n = 0;
while (!done && buf < bufEnd)
{
char c = *buf;
buf++;
// multirow
switch (c)
{
case '\\':
cont = true; // multirow
break;
case '\n':
if (start) break;
done = true;
break;
case '\r':
break;
case '\t':
case ' ':
if (start) break;
default:
start = false;
cont = false;
row[n++] = c;
if (n >= len-1)
done = true;
break;
}
}
row[n] = '\0';
return buf;
}
static int parseFace(char* row, int* data, int n, int vcnt)
{
int j = 0;
while (*row != '\0')
{
// Skip initial white space
while (*row != '\0' && (*row == ' ' || *row == '\t'))
row++;
char* s = row;
// Find vertex delimiter and terminated the string there for conversion.
while (*row != '\0' && *row != ' ' && *row != '\t')
{
if (*row == '/') *row = '\0';
row++;
}
if (*s == '\0')
continue;
int vi = atoi(s);
data[j++] = vi < 0 ? vi+vcnt : vi-1;
if (j >= n) return j;
}
return j;
}
bool rcMeshLoaderObj::load(const char* filename)
{
char* buf = 0;
FILE* fp = fopen(filename, "rb");
if (!fp)
return false;
fseek(fp, 0, SEEK_END);
int bufSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
buf = new char[bufSize];
if (!buf)
{
fclose(fp);
return false;
}
fread(buf, bufSize, 1, fp);
fclose(fp);
char* src = buf;
char* srcEnd = buf + bufSize;
char row[512];
int face[32];
float x,y,z;
int nv;
int vcap = 0;
int tcap = 0;
while (src < srcEnd)
{
// Parse one row
row[0] = '\0';
src = parseRow(src, srcEnd, row, sizeof(row)/sizeof(char));
// Skip comments
if (row[0] == '#') continue;
if (row[0] == 'v' && row[1] != 'n' && row[1] != 't')
{
// Vertex pos
sscanf(row+1, "%f %f %f", &x, &y, &z);
addVertex(x, y, z, vcap);
}
if (row[0] == 'f')
{
// Faces
nv = parseFace(row+1, face, 32, m_vertCount);
for (int i = 2; i < nv; ++i)
addTriangle(face[0], face[i-1], face[i], tcap);
}
}
delete [] buf;
// Calculate normals.
m_normals = new float[m_triCount*3];
for (int i = 0; i < m_triCount*3; i += 3)
{
const float* v0 = &m_verts[m_tris[i]*3];
const float* v1 = &m_verts[m_tris[i+1]*3];
const float* v2 = &m_verts[m_tris[i+2]*3];
float e0[3], e1[3];
for (int j = 0; j < 3; ++j)
{
e0[j] = v1[j] - v0[j];
e1[j] = v2[j] - v0[j];
}
float* n = &m_normals[i];
n[0] = e0[1]*e1[2] - e0[2]*e1[1];
n[1] = e0[2]*e1[0] - e0[0]*e1[2];
n[2] = e0[0]*e1[1] - e0[1]*e1[0];
float d = sqrtf(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);
if (d > 0)
{
d = 1.0f/d;
n[0] *= d;
n[1] *= d;
n[2] *= d;
}
}
return true;
}

View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef MESHLOADER_OBJ
#define MESHLOADER_OBJ
class rcMeshLoaderObj
{
public:
rcMeshLoaderObj();
~rcMeshLoaderObj();
bool load(const char* fileName);
inline const float* getVerts() const { return m_verts; }
inline const float* getNormals() const { return m_normals; }
inline const int* getTris() const { return m_tris; }
inline int getVertCount() const { return m_vertCount; }
inline int getTriCount() const { return m_triCount; }
private:
void addVertex(float x, float y, float z, int& cap);
void addTriangle(int a, int b, int c, int& cap);
float* m_verts;
int* m_tris;
float* m_normals;
int m_vertCount;
int m_triCount;
};
#endif // MESHLOADER_OBJ

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
Recast/Examples/Recast.exe Normal file

Binary file not shown.

BIN
Recast/Examples/SDL.dll Normal file

Binary file not shown.

11
Recast/Examples/SDLMain.h Normal file
View File

@ -0,0 +1,11 @@
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
Feel free to customize this file to suit your needs
*/
#import <Cocoa/Cocoa.h>
@interface SDLMain : NSObject
@end

384
Recast/Examples/SDLMain.m Normal file
View File

@ -0,0 +1,384 @@
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
Feel free to customize this file to suit your needs
*/
#import "SDL.h"
#import "SDLMain.h"
#import <sys/param.h> /* for MAXPATHLEN */
#import <unistd.h>
/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
but the method still is there and works. To avoid warnings, we declare
it ourselves here. */
@interface NSApplication(SDL_Missing_Methods)
- (void)setAppleMenu:(NSMenu *)menu;
@end
/* Use this flag to determine whether we use SDLMain.nib or not */
#define SDL_USE_NIB_FILE 0
/* Use this flag to determine whether we use CPS (docking) or not */
#define SDL_USE_CPS 1
#ifdef SDL_USE_CPS
/* Portions of CPS.h */
typedef struct CPSProcessSerNum
{
UInt32 lo;
UInt32 hi;
} CPSProcessSerNum;
extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
#endif /* SDL_USE_CPS */
static int gArgc;
static char **gArgv;
static BOOL gFinderLaunch;
static BOOL gCalledAppMainline = FALSE;
static NSString *getApplicationName(void)
{
NSDictionary *dict;
NSString *appName = 0;
/* Determine the application name */
dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
if (dict)
appName = [dict objectForKey: @"CFBundleName"];
if (![appName length])
appName = [[NSProcessInfo processInfo] processName];
return appName;
}
#if SDL_USE_NIB_FILE
/* A helper category for NSString */
@interface NSString (ReplaceSubString)
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
@end
#endif
@interface SDLApplication : NSApplication
@end
@implementation SDLApplication
/* Invoked from the Quit menu item */
- (void)terminate:(id)sender
{
/* Post a SDL_QUIT event */
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
}
@end
/* The main class of the application, the application's delegate */
@implementation SDLMain
/* Set the working directory to the .app's parent directory */
- (void) setupWorkingDirectory:(BOOL)shouldChdir
{
if (shouldChdir)
{
char parentdir[MAXPATHLEN];
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) {
assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
}
CFRelease(url);
CFRelease(url2);
}
}
#if SDL_USE_NIB_FILE
/* Fix menu to contain the real app name instead of "SDL App" */
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
{
NSRange aRange;
NSEnumerator *enumerator;
NSMenuItem *menuItem;
aRange = [[aMenu title] rangeOfString:@"SDL App"];
if (aRange.length != 0)
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
enumerator = [[aMenu itemArray] objectEnumerator];
while ((menuItem = [enumerator nextObject]))
{
aRange = [[menuItem title] rangeOfString:@"SDL App"];
if (aRange.length != 0)
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
if ([menuItem hasSubmenu])
[self fixMenu:[menuItem submenu] withAppName:appName];
}
[ aMenu sizeToFit ];
}
#else
static void setApplicationMenu(void)
{
/* warning: this code is very odd */
NSMenu *appleMenu;
NSMenuItem *menuItem;
NSString *title;
NSString *appName;
appName = getApplicationName();
appleMenu = [[NSMenu alloc] initWithTitle:@""];
/* Add menu items */
title = [@"About " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Hide " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
[appleMenu addItem:[NSMenuItem separatorItem]];
title = [@"Quit " stringByAppendingString:appName];
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
/* Put menu into the menubar */
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
[menuItem setSubmenu:appleMenu];
[[NSApp mainMenu] addItem:menuItem];
/* Tell the application object that this is now the application menu */
[NSApp setAppleMenu:appleMenu];
/* Finally give up our references to the objects */
[appleMenu release];
[menuItem release];
}
/* Create a window menu */
static void setupWindowMenu(void)
{
NSMenu *windowMenu;
NSMenuItem *windowMenuItem;
NSMenuItem *menuItem;
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
/* "Minimize" item */
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
[windowMenu addItem:menuItem];
[menuItem release];
/* Put menu into the menubar */
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
[windowMenuItem setSubmenu:windowMenu];
[[NSApp mainMenu] addItem:windowMenuItem];
/* Tell the application object that this is now the window menu */
[NSApp setWindowsMenu:windowMenu];
/* Finally give up our references to the objects */
[windowMenu release];
[windowMenuItem release];
}
/* Replacement for NSApplicationMain */
static void CustomApplicationMain (int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SDLMain *sdlMain;
/* Ensure the application object is initialised */
[SDLApplication sharedApplication];
#ifdef SDL_USE_CPS
{
CPSProcessSerNum PSN;
/* Tell the dock about us */
if (!CPSGetCurrentProcess(&PSN))
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
if (!CPSSetFrontProcess(&PSN))
[SDLApplication sharedApplication];
}
#endif /* SDL_USE_CPS */
/* Set up the menubar */
[NSApp setMainMenu:[[NSMenu alloc] init]];
setApplicationMenu();
setupWindowMenu();
/* Create SDLMain and make it the app delegate */
sdlMain = [[SDLMain alloc] init];
[NSApp setDelegate:sdlMain];
/* Start the main event loop */
[NSApp run];
[sdlMain release];
[pool release];
}
#endif
/*
* Catch document open requests...this lets us notice files when the app
* was launched by double-clicking a document, or when a document was
* dragged/dropped on the app's icon. You need to have a
* CFBundleDocumentsType section in your Info.plist to get this message,
* apparently.
*
* Files are added to gArgv, so to the app, they'll look like command line
* arguments. Previously, apps launched from the finder had nothing but
* an argv[0].
*
* This message may be received multiple times to open several docs on launch.
*
* This message is ignored once the app's mainline has been called.
*/
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
{
const char *temparg;
size_t arglen;
char *arg;
char **newargv;
if (!gFinderLaunch) /* MacOS is passing command line args. */
return FALSE;
if (gCalledAppMainline) /* app has started, ignore this document. */
return FALSE;
temparg = [filename UTF8String];
arglen = SDL_strlen(temparg) + 1;
arg = (char *) SDL_malloc(arglen);
if (arg == NULL)
return FALSE;
newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
if (newargv == NULL)
{
SDL_free(arg);
return FALSE;
}
gArgv = newargv;
SDL_strlcpy(arg, temparg, arglen);
gArgv[gArgc++] = arg;
gArgv[gArgc] = NULL;
return TRUE;
}
/* Called when the internal event loop has just started running */
- (void) applicationDidFinishLaunching: (NSNotification *) note
{
int status;
/* Set the working directory to the .app's parent directory */
[self setupWorkingDirectory:gFinderLaunch];
#if SDL_USE_NIB_FILE
/* Set the main menu to contain the real app name instead of "SDL App" */
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
#endif
/* Hand off to main application code */
gCalledAppMainline = TRUE;
status = SDL_main (gArgc, gArgv);
/* We're done, thank you for playing */
exit(status);
}
@end
@implementation NSString (ReplaceSubString)
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
{
unsigned int bufferSize;
unsigned int selfLen = [self length];
unsigned int aStringLen = [aString length];
unichar *buffer;
NSRange localRange;
NSString *result;
bufferSize = selfLen + aStringLen - aRange.length;
buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar));
/* Get first part into buffer */
localRange.location = 0;
localRange.length = aRange.location;
[self getCharacters:buffer range:localRange];
/* Get middle part into buffer */
localRange.location = 0;
localRange.length = aStringLen;
[aString getCharacters:(buffer+aRange.location) range:localRange];
/* Get last part into buffer */
localRange.location = aRange.location + aRange.length;
localRange.length = selfLen - localRange.location;
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
/* Build output string */
result = [NSString stringWithCharacters:buffer length:bufferSize];
NSDeallocateMemoryPages(buffer, bufferSize);
return result;
}
@end
#ifdef main
# undef main
#endif
/* Main entry point to executable - should *not* be SDL_main! */
int main (int argc, char **argv)
{
/* Copy the arguments into a global variable */
/* This is passed if we are launched by double-clicking */
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
gArgv[0] = argv[0];
gArgv[1] = NULL;
gArgc = 1;
gFinderLaunch = YES;
} else {
int i;
gArgc = argc;
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
for (i = 0; i <= argc; i++)
gArgv[i] = argv[i];
gFinderLaunch = NO;
}
#if SDL_USE_NIB_FILE
[SDLApplication poseAsClass:[NSApplication class]];
NSApplicationMain (argc, argv);
#else
CustomApplicationMain (argc, argv);
#endif
return 0;
}

796
Recast/Examples/demo.cpp Normal file
View File

@ -0,0 +1,796 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <float.h>
#ifdef WIN32
# include <io.h>
#else
# include <dirent.h>
#endif
#include "SDL.h"
#include "SDL_Opengl.h"
#include "GLFont.h"
#include "RecastTimer.h"
#include "MeshLoaderObj.h"
#include "Recast.h"
#include "RecastLog.h"
#include "RecastDebugDraw.h"
#include "imgui.h"
#ifdef WIN32
# define snprintf _snprintf
#endif
bool intersectSegmentTriangle(const float* sp, const float* sq,
const float* a, const float* b, const float* c,
float &t)
{
float v, w;
float ab[3], ac[3], qp[3], ap[3], norm[3], e[3];
vsub(ab, b, a);
vsub(ac, c, a);
vsub(qp, sp, sq);
// Compute triangle normal. Can be precalculated or cached if
// intersecting multiple segments against the same triangle
vcross(norm, ab, ac);
// Compute denominator d. If d <= 0, segment is parallel to or points
// away from triangle, so exit early
float d = vdot(qp, norm);
if (d <= 0.0f) return false;
// Compute intersection t value of pq with plane of triangle. A ray
// intersects iff 0 <= t. Segment intersects iff 0 <= t <= 1. Delay
// dividing by d until intersection has been found to pierce triangle
vsub(ap, sp, a);
t = vdot(ap, norm);
if (t < 0.0f) return false;
if (t > d) return false; // For segment; exclude this code line for a ray test
// Compute barycentric coordinate components and test if within bounds
vcross(e, qp, ap);
v = vdot(ac, e);
if (v < 0.0f || v > d) return false;
w = -vdot(ab, e);
if (w < 0.0f || v + w > d) return false;
// Segment/ray intersects triangle. Perform delayed division
t /= d;
return true;
}
static bool raycast(rcMeshLoaderObj& mesh, float* src, float* dst, float& tmin)
{
float dir[3];
vsub(dir, dst, src);
int nt = mesh.getTriCount();
const float* verts = mesh.getVerts();
const float* normals = mesh.getNormals();
const int* tris = mesh.getTris();
tmin = 1.0f;
bool hit = false;
for (int i = 0; i < nt*3; i += 3)
{
const float* n = &normals[i];
if (vdot(dir, n) > 0)
continue;
float t = 1;
if (intersectSegmentTriangle(src, dst,
&verts[tris[i]*3],
&verts[tris[i+1]*3],
&verts[tris[i+2]*3], t))
{
if (t < tmin)
tmin = t;
hit = true;
}
}
return hit;
}
struct FileList
{
static const int MAX_FILES = 256;
inline FileList() : size(0) {}
inline ~FileList()
{
clear();
}
void clear()
{
for (int i = 0; i < size; ++i)
delete [] files[i];
size = 0;
}
void add(const char* path)
{
if (size >= MAX_FILES)
return;
int n = strlen(path);
files[size] = new char[n+1];
strcpy(files[size], path);
size++;
}
char* files[MAX_FILES];
int size;
};
void scanDirectory(const char* path, const char* ext, FileList& list)
{
list.clear();
#ifdef WIN32
_finddata_t dir;
char pathWithExt[MAX_PATH];
long fh;
strcpy(pathWithExt, path);
strcat(pathWithExt, "/*");
strcat(pathWithExt, ext);
fh = _findfirst(pathWithExt, &dir);
if (fh == -1L)
return;
do
{
list.add(dir.name);
}
while (_findnext(fh, &dir) == 0);
_findclose(fh);
#else
dirent* current = 0;
DIR* dp = opendir(path);
if (!dp)
return;
while ((current = readdir(dp)) != 0)
{
int len = strlen(current->d_name);
if (len > 4 && strncmp(current->d_name+len-4, ext, 4) == 0)
{
list.add(current->d_name);
}
}
closedir(dp);
#endif
}
enum DrawMode
{
DRAWMODE_POLYMESH,
DRAWMODE_POLYMESH_TRANS,
DRAWMODE_MESH,
DRAWMODE_VOXELS,
DRAWMODE_VOXELS_WALKABLE,
DRAWMODE_COMPACT,
DRAWMODE_COMPACT_DISTANCE,
DRAWMODE_COMPACT_REGIONS,
DRAWMODE_RAW_CONTOURS,
DRAWMODE_CONTOURS,
MAX_DRAWMODE,
};
GLFont g_font;
void drawText(int x, int y, int dir, const char* text, unsigned int col)
{
if (dir < 0)
g_font.drawText((float)x - g_font.getTextLength(text), (float)y, text, col);
else
g_font.drawText((float)x, (float)y, text, col);
}
rcMeshLoaderObj* g_mesh = 0;
unsigned char* g_triangleFlags = 0;
rcHeightfield* g_solid = 0;
rcCompactHeightfield* g_chf = 0;
rcContourSet* g_cset = 0;
rcPolyMesh* g_polyMesh = 0;
rcConfig g_cfg;
rcLog g_log;
static bool buildNavigation()
{
delete g_solid;
delete g_chf;
delete g_cset;
delete g_polyMesh;
delete [] g_triangleFlags;
g_solid = 0;
g_chf = 0;
g_cset = 0;
g_polyMesh = 0;
g_triangleFlags = 0;
g_log.clear();
rcSetLog(&g_log);
if (!g_mesh)
{
g_log.log(RC_LOG_ERROR, "Input mesh is not valid.");
return false;
}
rcTimeVal startTime = rcGetPerformanceTimer();
rcCalcBounds(g_mesh->getVerts(), g_mesh->getVertCount(), g_cfg.bmin, g_cfg.bmax);
rcCalcGridSize(g_cfg.bmin, g_cfg.bmax, g_cfg.cs, &g_cfg.width, &g_cfg.height);
g_log.log(RC_LOG_PROGRESS, "Building navigation");
g_log.log(RC_LOG_PROGRESS, " - %d x %d", g_cfg.width, g_cfg.height);
g_log.log(RC_LOG_PROGRESS, " - %d verts, %d tris", g_mesh->getVertCount(), g_mesh->getTriCount());
g_triangleFlags = new unsigned char[g_mesh->getTriCount()];
memset(g_triangleFlags, 0, g_mesh->getTriCount());
rcMarkWalkableTriangles(g_cfg.walkableSlopeAngle,
g_mesh->getTris(), g_mesh->getNormals(), g_mesh->getTriCount(),
g_triangleFlags);
g_solid = new rcHeightfield;
g_chf = new rcCompactHeightfield;
g_cset = new rcContourSet;
g_polyMesh = new rcPolyMesh;
if (!rcBuildNavMesh(g_cfg, g_mesh->getVerts(), g_mesh->getVertCount(),
g_mesh->getTris(), g_triangleFlags, g_mesh->getTriCount(),
*g_solid, *g_chf, *g_cset, *g_polyMesh))
{
g_log.log(RC_LOG_ERROR, "Could not build navmesh.");
return false;
}
rcTimeVal endTime = rcGetPerformanceTimer();
g_log.log(RC_LOG_PROGRESS, "Build time: %.1f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
g_log.log(RC_LOG_PROGRESS, "NavMesh");
g_log.log(RC_LOG_PROGRESS, " - %d verts, %d polys", g_polyMesh->nverts, g_polyMesh->npolys);
const int navMeshDataSize = g_polyMesh->nverts*3*sizeof(unsigned short) +
g_polyMesh->npolys*g_polyMesh->nvp*2*sizeof(unsigned short);
g_log.log(RC_LOG_PROGRESS, " - Approx data size %.1f kB", (float)navMeshDataSize/1024.f);
for (int i = 0; i < g_log.getMessageCount(); ++i)
{
printf("%s\n", g_log.getMessageText(i));
}
return true;
}
int main(int argc, char *argv[])
{
// Init SDL
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
printf("Could not initialise SDL\n");
return -1;
}
// Init OpenGL
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
int width = 1200;
int height = 700;
SDL_Surface* screen = SDL_SetVideoMode(width, height, 0, SDL_OPENGL);
if (!screen)
{
printf("Could not initialise SDL opengl\n");
return -1;
}
SDL_WM_SetCaption("Recast Demo", 0);
if(!g_font.create("font.cfnt"))
{
printf("Could not load font\n");
SDL_Quit();
return -1;
}
float cellSize = 0.3f;
float cellHeight = 0.2f;
float agentHeight = 2.0f;
float agentRadius = 0.3f;
float agentMaxClimb = 0.9f;
float agentMaxSlope = 45.0f;
float regionMinSize = 50;
float regionMergeSize = 20;
float edgeMaxLen = 12.0f;
float edgeMaxError = 1.5f;
float vertsPerPoly = 6.0f;
int drawMode = DRAWMODE_POLYMESH;
bool showLevels = false;
bool showLog = false;
char curLevel[256] = "Choose Level...";
bool mouseOverMenu = false;
FileList fileList;
float t = 0.0f;
Uint32 lastTime = SDL_GetTicks();
int mx = 0, my = 0;
float rx = 45;
float ry = -45;
float moveW = 0, moveS = 0, moveA = 0, moveD = 0;
float camx = 0, camy = 0, camz = 0, camr=10;
float origrx, origry;
int origx, origy;
bool rotate = false;
float rays[3], raye[3];
float spos[3] = {0,0,0};
glEnable(GL_CULL_FACE);
float fogCol[4] = { 0.1f,0.12f,0.14f,1 };
glEnable(GL_FOG);
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogf(GL_FOG_START, 0);
glFogf(GL_FOG_END, 10);
glFogfv(GL_FOG_COLOR, fogCol);
bool done = false;
while(!done)
{
// Handle input events.
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_KEYDOWN:
// Handle any key presses here.
if(event.key.keysym.sym == SDLK_ESCAPE)
{
done = true;
}
break;
case SDL_MOUSEBUTTONDOWN:
// Handle mouse clicks here.
if (!mouseOverMenu)
{
if (event.button.button == SDL_BUTTON_LEFT)
{
// Rotate view
rotate = true;
origx = mx;
origy = my;
origrx = rx;
origry = ry;
}
else if (event.button.button == SDL_BUTTON_RIGHT)
{
// Hit test mesh.
if (g_mesh)
{
float t;
if (raycast(*g_mesh, rays, raye, t))
{
spos[0] = rays[0] + (raye[0] - rays[0])*t;
spos[1] = rays[1] + (raye[1] - rays[1])*t;
spos[2] = rays[2] + (raye[2] - rays[2])*t;
}
}
}
}
break;
case SDL_MOUSEBUTTONUP:
// Handle mouse clicks here.
if(event.button.button == SDL_BUTTON_LEFT)
{
rotate = false;
}
break;
case SDL_MOUSEMOTION:
mx = event.motion.x;
my = height - 1 - event.motion.y;
if (rotate)
{
int dx = mx - origx;
int dy = my - origy;
rx = origrx - dy*0.25f;
ry = origry + dx*0.25f;
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
Uint32 time = SDL_GetTicks();
float dt = (time - lastTime) / 1000.0f;
lastTime = time;
t += dt;
// Update and render
glViewport(0, 0, width, height);
glClearColor(0.3f, 0.3f, 0.32f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
// Render 3d
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50.0f, (float)width/(float)height, 1.0f, camr);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(rx,1,0,0);
glRotatef(ry,0,1,0);
glTranslatef(-camx, -camy, -camz);
// Get hit ray position and direction.
GLdouble proj[16];
GLdouble model[16];
GLint view[4];
glGetDoublev(GL_PROJECTION_MATRIX, proj);
glGetDoublev(GL_MODELVIEW_MATRIX, model);
glGetIntegerv(GL_VIEWPORT, view);
GLdouble x, y, z;
gluUnProject(mx, my, 0.0f, model, proj, view, &x, &y, &z);
rays[0] = (float)x; rays[1] = (float)y; rays[2] = (float)z;
gluUnProject(mx, my, 1.0f, model, proj, view, &x, &y, &z);
raye[0] = (float)x; raye[1] = (float)y; raye[2] = (float)z;
// Handle keyboard movement.
Uint8* keystate = SDL_GetKeyState(NULL);
moveW = rcClamp(moveW + dt * 4 * (keystate[SDLK_w] ? 1 : -1), 0.0f, 1.0f);
moveS = rcClamp(moveS + dt * 4 * (keystate[SDLK_s] ? 1 : -1), 0.0f, 1.0f);
moveA = rcClamp(moveA + dt * 4 * (keystate[SDLK_a] ? 1 : -1), 0.0f, 1.0f);
moveD = rcClamp(moveD + dt * 4 * (keystate[SDLK_d] ? 1 : -1), 0.0f, 1.0f);
float keybSpeed = 22.0f;
if (SDL_GetModState() & KMOD_SHIFT)
keybSpeed *= 4.0f;
float movex = (moveD - moveA) * keybSpeed * dt;
float movey = (moveS - moveW) * keybSpeed * dt;
camx += movex * (float)model[0];
camy += movex * (float)model[4];
camz += movex * (float)model[8];
camx += movey * (float)model[2];
camy += movey * (float)model[6];
camz += movey * (float)model[10];
glEnable(GL_FOG);
if (drawMode == DRAWMODE_MESH)
{
if (g_mesh)
rcDebugDrawMesh(*g_mesh, g_triangleFlags);
}
else if (drawMode != DRAWMODE_POLYMESH_TRANS)
{
if (g_mesh)
rcDebugDrawMesh(*g_mesh, 0);
}
glDepthMask(GL_FALSE);
if (drawMode == DRAWMODE_POLYMESH || drawMode == DRAWMODE_POLYMESH_TRANS)
{
if (g_polyMesh)
rcDebugDrawPolyMesh(*g_polyMesh, g_cfg.bmin, g_cfg.cs, g_cfg.ch);
}
glDepthMask(GL_TRUE);
if (drawMode == DRAWMODE_COMPACT)
{
if (g_chf)
rcDebugDrawCompactHeightfieldSolid(*g_chf);
}
if (drawMode == DRAWMODE_COMPACT_DISTANCE)
{
if (g_chf)
rcDebugDrawCompactHeightfieldDistance(*g_chf);
}
if (drawMode == DRAWMODE_COMPACT_REGIONS)
{
if (g_chf)
rcDebugDrawCompactHeightfieldRegions(*g_chf);
}
if (drawMode == DRAWMODE_VOXELS)
{
if (g_solid)
rcDebugDrawHeightfieldSolid(*g_solid, g_cfg.bmin, g_cfg.cs, g_cfg.ch);
}
if (drawMode == DRAWMODE_VOXELS_WALKABLE)
{
if (g_solid)
rcDebugDrawHeightfieldWalkable(*g_solid, g_cfg.bmin, g_cfg.cs, g_cfg.ch);
}
if (drawMode == DRAWMODE_RAW_CONTOURS)
{
if (g_cset)
rcDebugDrawRawContours(*g_cset, g_cfg.bmin, g_cfg.cs, g_cfg.ch);
}
if (drawMode == DRAWMODE_CONTOURS)
{
if (g_cset)
rcDebugDrawContours(*g_cset, g_cfg.bmin, g_cfg.cs, g_cfg.ch);
}
glDisable(GL_FOG);
if (g_mesh)
{
// Agent dimensions.
const float r = agentRadius;
const float h = agentHeight;
float col[4];
col[0] = 0.6f; col[1] = 0.1f; col[2] = 0.1f; col[3] = 0.75f;
rcDebugDrawCylinderWire(spos[0]-r, spos[1]+0.02f, spos[2]-r, spos[0]+r, spos[1]+h, spos[2]+r, col);
glColor4ub(0,0,0,196);
glBegin(GL_LINES);
glVertex3f(spos[0], spos[1]-agentMaxClimb, spos[2]);
glVertex3f(spos[0], spos[1]+agentMaxClimb, spos[2]);
glVertex3f(spos[0]-r/2, spos[1]+0.02f, spos[2]);
glVertex3f(spos[0]+r/2, spos[1]+0.02f, spos[2]);
glVertex3f(spos[0], spos[1]+0.02f, spos[2]-r/2);
glVertex3f(spos[0], spos[1]+0.02f, spos[2]+r/2);
glEnd();
// Mesh bbox.
col[0] = 1.0f; col[1] = 1.0f; col[2] = 1.0f; col[3] = 0.25f;
rcDebugDrawBoxWire(g_cfg.bmin[0], g_cfg.bmin[1], g_cfg.bmin[2],
g_cfg.bmax[0], g_cfg.bmax[1], g_cfg.bmax[2], col);
}
// Render GUI
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
imguiBeginFrame();
mouseOverMenu = false;
static int propScroll = 0;
if (imguiBeginScrollArea(GENID, "Properties", width - 250 - 10, 10, 250, height-20, &propScroll))
mouseOverMenu = true;
if (imguiButton(GENID, curLevel))
{
showLevels = true;
scanDirectory("meshes", ".obj", fileList);
}
imguiSeparator();
if (g_mesh)
{
if (imguiButton(GENID, "Build"))
{
memset(&g_cfg, 0, sizeof(g_cfg));
g_cfg.cs = cellSize;
g_cfg.ch = cellHeight;
g_cfg.walkableSlopeAngle = agentMaxSlope;
g_cfg.walkableHeight = (int)ceilf(agentHeight / g_cfg.ch);
g_cfg.walkableClimb = (int)ceilf(agentMaxClimb / g_cfg.ch);
g_cfg.walkableRadius = (int)ceilf(agentRadius / g_cfg.cs);
g_cfg.maxEdgeLen = (int)(edgeMaxLen / cellSize);
g_cfg.maxSimplificationError = edgeMaxError;
g_cfg.minRegionSize = (int)rcSqr(regionMinSize);
g_cfg.mergeRegionSize = (int)rcSqr(regionMergeSize);
g_cfg.maxVertsPerPoly = (int)vertsPerPoly;
buildNavigation();
}
}
imguiSeparator();
if (imguiCheck(GENID, "Show Log", showLog))
showLog = !showLog;
imguiSeparator();
imguiLabel(GENID, "Rasterization");
imguiSlider(GENID, "Cell Size", &cellSize, 0.1f, 1.0f, 0.01f);
imguiSlider(GENID, "Cell Height", &cellHeight, 0.1f, 1.0f, 0.01f);
if (g_mesh)
{
int gw = 0, gh = 0;
rcCalcGridSize(g_cfg.bmin, g_cfg.bmax, cellSize, &gw, &gh);
char text[64];
snprintf(text, 64, "Grid %d x %d", gw, gh);
imguiValue(GENID, text);
}
imguiSeparator();
imguiLabel(GENID, "Agent");
imguiSlider(GENID, "Height", &agentHeight, 0.1f, 5.0f, 0.1f);
imguiSlider(GENID, "Ragius", &agentRadius, 0.1f, 5.0f, 0.1f);
imguiSlider(GENID, "Max Climb", &agentMaxClimb, 0.1f, 5.0f, 0.1f);
imguiSlider(GENID, "Max Slope", &agentMaxSlope, 0.0f, 90.0f, 1.0f);
imguiSeparator();
imguiLabel(GENID, "Region");
imguiSlider(GENID, "Min Region Size", &regionMinSize, 0.0f, 150.0f, 1.0f);
imguiSlider(GENID, "Merged Region Size", &regionMergeSize, 0.0f, 150.0f, 1.0f);
imguiSeparator();
imguiLabel(GENID, "Polygonization");
imguiSlider(GENID, "Max Edge Length", &edgeMaxLen, 0.0f, 50.0f, 1.0f);
imguiSlider(GENID, "Max Edge Error", &edgeMaxError, 0.1f, 3.0f, 0.1f);
imguiSlider(GENID, "Verts Per Poly", &vertsPerPoly, 3.0f, 12.0f, 1.0f);
imguiSeparator();
imguiLabel(GENID, "Draw");
if (imguiCheck(GENID, "Input Mesh", drawMode == DRAWMODE_MESH))
drawMode = DRAWMODE_MESH;
if (imguiCheck(GENID, "Navmesh", drawMode == DRAWMODE_POLYMESH))
drawMode = DRAWMODE_POLYMESH;
if (imguiCheck(GENID, "Navmesh Trans", drawMode == DRAWMODE_POLYMESH_TRANS))
drawMode = DRAWMODE_POLYMESH_TRANS;
if (imguiCheck(GENID, "Voxels", drawMode == DRAWMODE_VOXELS))
drawMode = DRAWMODE_VOXELS;
if (imguiCheck(GENID, "Walkable Voxels", drawMode == DRAWMODE_VOXELS_WALKABLE))
drawMode = DRAWMODE_VOXELS_WALKABLE;
if (imguiCheck(GENID, "Compact", drawMode == DRAWMODE_COMPACT))
drawMode = DRAWMODE_COMPACT;
if (imguiCheck(GENID, "Compact Distance", drawMode == DRAWMODE_COMPACT_DISTANCE))
drawMode = DRAWMODE_COMPACT_DISTANCE;
if (imguiCheck(GENID, "Compact Regions", drawMode == DRAWMODE_COMPACT_REGIONS))
drawMode = DRAWMODE_COMPACT_REGIONS;
if (imguiCheck(GENID, "Raw Contours", drawMode == DRAWMODE_RAW_CONTOURS))
drawMode = DRAWMODE_RAW_CONTOURS;
if (imguiCheck(GENID, "Contours", drawMode == DRAWMODE_CONTOURS))
drawMode = DRAWMODE_CONTOURS;
imguiEndScrollArea();
// Log
if (showLog)
{
static int logScroll = 0;
if (imguiBeginScrollArea(GENID, "Log", 10, 10, width - 300, 200, &logScroll))
mouseOverMenu = true;
for (int i = 0; i < g_log.getMessageCount(); ++i)
imguiLabel(GENID1(i), g_log.getMessageText(i));
imguiEndScrollArea();
}
// Level selection dialog.
if (showLevels)
{
static int scroll = 0;
if (imguiBeginScrollArea(GENID, "Choose Level", width-10-250-10-200, height-10-250, 200, 250, &scroll))
mouseOverMenu = true;
int levelToLoad = -1;
for (int i = 0; i < fileList.size; ++i)
{
if (imguiItem(GENID1(i), fileList.files[i]))
levelToLoad = i;
}
if (levelToLoad != -1)
{
strncpy(curLevel, fileList.files[levelToLoad], sizeof(curLevel));
curLevel[sizeof(curLevel)-1] = '\0';
showLevels = false;
delete g_mesh;
delete g_solid;
delete g_chf;
delete g_cset;
delete g_polyMesh;
delete [] g_triangleFlags;
g_mesh = 0;
g_solid = 0;
g_chf = 0;
g_cset = 0;
g_polyMesh = 0;
g_triangleFlags = 0;
g_mesh = new rcMeshLoaderObj;
char path[256];
strcpy(path, "meshes/");
strcat(path, curLevel);
if (!g_mesh->load(path))
{
printf("Could not load mesh\n");
delete g_mesh;
g_mesh = 0;
}
if (g_mesh)
{
rcCalcBounds(g_mesh->getVerts(), g_mesh->getVertCount(), g_cfg.bmin, g_cfg.bmax);
// Reset camera.
camr = sqrtf(rcSqr(g_cfg.bmax[0]-g_cfg.bmin[0]) +
rcSqr(g_cfg.bmax[1]-g_cfg.bmin[1]) +
rcSqr(g_cfg.bmax[2]-g_cfg.bmin[2])) / 2;
camx = (g_cfg.bmax[0] + g_cfg.bmin[0]) / 2 + camr;
camy = (g_cfg.bmax[1] + g_cfg.bmin[1]) / 2 + camr;
camz = (g_cfg.bmax[2] + g_cfg.bmin[2]) / 2 + camr;
camr *= 3;
rx = 45;
ry = -45;
glFogf(GL_FOG_START, camr*0.5f);
glFogf(GL_FOG_END, camr*2.5f);
}
}
imguiEndScrollArea();
}
imguiEndFrame();
imguiRender(&drawText);
g_font.drawText(10.0f, (float)height-20.0f, "W/S/A/D: Move LMB: Rotate RMB: Place character", GLFont::RGBA(255,255,255,128));
glEnable(GL_DEPTH_TEST);
SDL_GL_SwapBuffers();
}
SDL_Quit();
delete g_mesh;
delete g_solid;
delete g_chf;
delete g_cset;
delete g_polyMesh;
delete [] g_triangleFlags;
return 0;
}

BIN
Recast/Examples/font.cfnt Normal file

Binary file not shown.

285
Recast/Examples/glfont.cpp Normal file
View File

@ -0,0 +1,285 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include "GlFont.h"
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <SDL_Opengl.h>
#include <stdlib.h>
GLFont::GLFont(int renderVerts) :
m_fd(0),
m_texId(0),
m_verts(0),
m_nverts(0),
m_maxVerts(renderVerts)
{
}
GLFont::~GLFont()
{
if (m_texId)
glDeleteTextures(1, (GLuint*)&m_texId);
unsigned char* data = (unsigned char*)m_fd;
if (data)
free(data);
if (m_verts)
free(m_verts);
}
bool GLFont::create(const char* fileName)
{
unsigned char* data = 0;
FILE* fp = fopen(fileName, "rb");
if (!fp)
return false;
// Read cache file
fseek(fp, 0, SEEK_END);
unsigned n = ftell(fp);
fseek(fp, 0, SEEK_SET);
data = (unsigned char*)malloc(n);
fread(data, n, 1, fp);
fclose(fp);
if (!m_verts)
m_verts = (RenderVertex*)malloc(m_maxVerts*sizeof(RenderVertex));
return createFontFromFontData(data);
}
bool GLFont::createFontFromFontData(unsigned char* data)
{
if (!data)
{
printf("GLFont::createFontFromFontData: No input data!\n");
return false;
}
m_fd = (FontData*)data;
// Patch kern pointers.
for (int i = 0; i < m_fd->charCount; ++i)
m_fd->glyphs[i].kern = (KerningPair*)((int)m_fd->glyphs[i].kernOffset + data);
unsigned char* texData = data + m_fd->textureOffset;
// Create textures
glEnable(GL_TEXTURE_2D);
glGenTextures(1, (GLuint*)&m_texId);
glBindTexture(GL_TEXTURE_2D, m_texId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, m_fd->texWidth, m_fd->texHeight, 0,
GL_ALPHA, GL_UNSIGNED_BYTE, texData);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
return true;
}
int GLFont::getFontSize() const
{
return m_fd ? m_fd->fontSize : 0;
}
int GLFont::getDescender() const
{
return m_fd ? m_fd->descender : 0;
}
int GLFont::getAscender() const
{
return m_fd ? m_fd->ascender : 0;
}
float GLFont::getLineHeight() const
{
return m_fd ? m_fd->lineHeight : 0.0f;
}
float GLFont::getTextLength(const char* text, float size, float tracking)
{
if (!m_texId) return 0.0f;
if (!m_fd) return 0.0f;
float scale = size < 0 ? 1 : size / (float)m_fd->fontSize;
float track = scale * m_fd->ascender * tracking / 1000.0f;
const unsigned char* src = (const unsigned char*)text;
int prevc = -1;
float len = 0.0f;
float tx = 0.0f;
for (; *src; ++src)
{
int c = (int)*src - m_fd->charMin;
if (c < 0 || c >= m_fd->charCount)
{
prevc = c;
continue;
}
CachedGlyph& cg = m_fd->glyphs[c];
if (prevc > 0 && prevc < m_fd->charCount)
{
CachedGlyph& prevcg = m_fd->glyphs[prevc];
if (prevcg.nkern != 0)
{
for (int i = 0; i < prevcg.nkern; ++i)
{
if (prevcg.kern[i].c == c)
{
tx += prevcg.kern[i].dx * scale;
break;
}
}
}
}
len = tx + (cg.ox + cg.w) * scale;
tx += cg.adv * scale + track;
prevc = c;
}
return len;
}
void GLFont::drawText(float tx, float ty, const char* text,
unsigned int col, float size, float tracking)
{
if (!m_fd) return;
if (!m_texId) return;
if (!m_verts) return;
float scale = size < 0 ? 1 : size / (float)m_fd->fontSize;
float track = scale * m_fd->ascender * tracking / 1000.0f;
float su = 1.0f / m_fd->texWidth;
float sv = 1.0f / m_fd->texHeight;
const unsigned char* src = (const unsigned char*)text;
RenderVertex* v = &m_verts[m_nverts];
int prevc = -1;
for (; *src; ++src)
{
int c = (int)*src - m_fd->charMin;
if (c == '\n')
{
ty -= getLineHeight();
prevc = -1;
continue;
}
if (c < 0 || c >= m_fd->charCount)
{
prevc = c;
continue;
}
CachedGlyph& cg = m_fd->glyphs[c];
if (prevc > 0 && prevc < m_fd->charCount)
{
CachedGlyph& prevcg = m_fd->glyphs[prevc];
if (prevcg.nkern != 0)
{
for (int i = 0; i < prevcg.nkern; ++i)
{
if (prevcg.kern[i].c == c)
{
tx += prevcg.kern[i].dx * scale;
break;
}
}
}
}
float x0 = floorf(tx + (cg.ox - 1) * scale + 0.5f);
float y0 = floorf(ty + (cg.oy - 1) * scale + 0.5f);
float x1 = floorf(x0 + (cg.w + 2) * scale + 0.5f);
float y1 = floorf(y0 + (cg.h + 2) * scale + 0.5f);
float u0 = (cg.tx - 1) * su;
float v0 = (cg.ty - 1) * sv;
float u1 = (cg.tx + cg.w + 1) * su;
float v1 = (cg.ty + cg.h + 1) * sv;
if (m_nverts+6 > m_maxVerts) break;
v->set(x0, y0, u0, v0, col); v++;
v->set(x1, y0, u1, v0, col); v++;
v->set(x1, y1, u1, v1, col); v++;
v->set(x0, y0, u0, v0, col); v++;
v->set(x1, y1, u1, v1, col); v++;
v->set(x0, y1, u0, v1, col); v++;
m_nverts += 6;
tx += cg.adv * scale + track;
prevc = c;
}
render();
}
void GLFont::render()
{
if (!m_fd) return;
if (!m_texId) return;
if (!m_verts) return;
// Render
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_texId);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glDisableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(RenderVertex), &m_verts[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(RenderVertex), &m_verts[0].u);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(RenderVertex), &m_verts[0].col);
glDrawArrays(GL_TRIANGLES, 0, m_nverts);
m_nverts = 0;
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisable(GL_TEXTURE_2D);
}
unsigned int GLFont::RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
return (a<<24) | (b<<16) | (g<<8) | r;
/*#ifdef WIN32
return (a<<24) | (b<<16) | (g<<8) | r;
#else
return (r<<24) | (g<<16) | (b<<8) | a;
#endif*/
}

106
Recast/Examples/glfont.h Normal file
View File

@ -0,0 +1,106 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef GLFONT_H
#define GLFONT_H
class GLFont
{
public:
GLFont(int renderVerts = 4096);
~GLFont();
bool create(const char* fileName);
int getFontSize() const;
int getDescender() const;
int getAscender() const;
float getLineHeight() const;
float getTextLength(const char* text, float size = -1, float tracking = 0);
void drawText(float x, float y, const char* text,
unsigned int col, float size = -1, float tracking = 0);
void render();
void debugDraw();
static unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255);
private:
bool createFontFromFontData(unsigned char* fd);
struct KerningPair
{
inline KerningPair() {}
inline KerningPair(unsigned char c_, float dx_) : dx(dx_), c(c_) {}
inline void Set(unsigned char c_, float dx_) { dx = dx_; c = c_; }
float dx;
unsigned char c, pad[3];
};
struct CachedGlyph
{
inline CachedGlyph() : w(0), h(0), ox(0), oy(0), tx(0), ty(0), adv(0.0f), nkern(0), kern(0) {}
int w, h;
int ox, oy;
int tx, ty;
float adv;
int nkern;
union
{
KerningPair* kern;
int kernOffset;
};
};
struct FontData
{
unsigned int endian;
unsigned int version;
unsigned int dataSize;
unsigned int kernOffset;
unsigned int textureOffset;
int fontSize;
unsigned int texWidth;
unsigned int texHeight;
int numMipmaps;
int ascender;
int descender;
int lineHeight;
int charMin;
int charCount;
CachedGlyph glyphs[1];
};
FontData* m_fd;
unsigned int m_texId;
struct RenderVertex
{
inline void set(float x_, float y_, float u_, float v_, unsigned int c) { x=x_; y=y_; u=u_; v=v_; col=c; }
float x, y, u, v;
unsigned int col;
};
RenderVertex* m_verts;
int m_nverts;
const int m_maxVerts;
};
#endif // GLFONT_H

851
Recast/Examples/imgui.cpp Normal file
View File

@ -0,0 +1,851 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include <string.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include "imgui.h"
#include "SDL.h"
#include "SDL_opengl.h"
#ifdef WIN32
# define snprintf _snprintf
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
enum GfxCmdType
{
GFXCMD_RECT,
GFXCMD_TRIANGLE,
GFXCMD_TEXT,
GFXCMD_SCISSOR,
};
struct GfxRect
{
short x,y,w,h,r;
};
struct GfxText
{
short x,y,dir;
const char* text;
};
struct GfxCmd
{
char type;
char flags;
char pad[2];
unsigned int col;
union
{
GfxRect rect;
GfxText text;
};
};
unsigned int RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
return (r) | (g << 8) | (b << 16) | (a << 24);
}
static const unsigned TEXT_POOL_SIZE = 4096;
static char g_textPool[TEXT_POOL_SIZE];
static unsigned g_textPoolSize = 0;
const char* allocText(const char* text)
{
unsigned len = strlen(text)+1;
if (g_textPoolSize + len >= TEXT_POOL_SIZE)
return 0;
char* dst = &g_textPool[g_textPoolSize];
memcpy(dst, text, len);
g_textPoolSize += len;
return dst;
}
static const unsigned GFXCMD_QUEUE_SIZE = 1024;
static GfxCmd g_gfxCmdQueue[GFXCMD_QUEUE_SIZE];
static unsigned g_gfxCmdQueueSize = 0;
void resetGfxCmdQueue()
{
g_gfxCmdQueueSize = 0;
g_textPoolSize = 0;
}
static const unsigned TEMP_COORD_COUNT = 100;
static float g_tempCoords[TEMP_COORD_COUNT*2];
static float g_tempNormals[TEMP_COORD_COUNT*2];
static void drawPolygon(const float* coords, unsigned numCoords, float r, unsigned int col)
{
if (numCoords > TEMP_COORD_COUNT) numCoords = TEMP_COORD_COUNT;
for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
{
const float* v0 = &coords[j*2];
const float* v1 = &coords[i*2];
float dx = v1[0] - v0[0];
float dy = v1[1] - v0[1];
float d = sqrtf(dx*dx+dy*dy);
if (d > 0)
{
d = 1.0f/d;
dx *= d;
dy *= d;
}
g_tempNormals[j*2+0] = dy;
g_tempNormals[j*2+1] = -dx;
}
for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
{
float dlx0 = g_tempNormals[j*2+0];
float dly0 = g_tempNormals[j*2+1];
float dlx1 = g_tempNormals[i*2+0];
float dly1 = g_tempNormals[i*2+1];
float dmx = (dlx0 + dlx1) * 0.5f;
float dmy = (dly0 + dly1) * 0.5f;
float dmr2 = dmx*dmx + dmy*dmy;
if (dmr2 > 0.000001f)
{
float scale = 1.0f / dmr2;
if (scale > 10.0f) scale = 10.0f;
dmx *= scale;
dmy *= scale;
}
g_tempCoords[i*2+0] = coords[i*2+0]+dmx*r;
g_tempCoords[i*2+1] = coords[i*2+1]+dmy*r;
}
unsigned int colTrans = RGBA(col&0xff, (col>>8)&0xff, (col>>16)&0xff, 0);
glBegin(GL_TRIANGLES);
glColor4ubv((GLubyte*)&col);
for (unsigned i = 0, j = numCoords-1; i < numCoords; j=i++)
{
glVertex2fv(&coords[i*2]);
glVertex2fv(&coords[j*2]);
glColor4ubv((GLubyte*)&colTrans);
glVertex2fv(&g_tempCoords[j*2]);
glVertex2fv(&g_tempCoords[j*2]);
glVertex2fv(&g_tempCoords[i*2]);
glColor4ubv((GLubyte*)&col);
glVertex2fv(&coords[i*2]);
}
glColor4ubv((GLubyte*)&col);
for (unsigned i = 2; i < numCoords; ++i)
{
glVertex2fv(&coords[0]);
glVertex2fv(&coords[(i-1)*2]);
glVertex2fv(&coords[i*2]);
}
glEnd();
}
static const int CIRCLE_VERTS = 8*4;
static float g_circleVerts[CIRCLE_VERTS*2];
static bool g_circleVertsInitialized = false;
const float* getCircleVerts()
{
if (!g_circleVertsInitialized)
{
g_circleVertsInitialized = true;
for (unsigned i = 0; i < CIRCLE_VERTS; ++i)
{
float a = (float)i/(float)CIRCLE_VERTS * (float)M_PI*2;
g_circleVerts[i*2+0] = cosf(a);
g_circleVerts[i*2+1] = sinf(a);
}
}
return g_circleVerts;
}
static void drawRect(float x, float y, float w, float h, float fth, unsigned int col)
{
float verts[4*2] =
{
x, y,
x+w, y,
x+w, y+h,
x, y+h,
};
drawPolygon(verts, 4, fth, col);
}
static void drawEllipse(float x, float y, float w, float h, float fth, unsigned int col)
{
float verts[CIRCLE_VERTS*2];
const float* cverts = getCircleVerts();
float* v = verts;
for (unsigned i = 0; i < CIRCLE_VERTS; ++i)
{
*v++ = x + cverts[i*2]*w;
*v++ = y + cverts[i*2+1]*h;
}
drawPolygon(verts, CIRCLE_VERTS, fth, col);
}
static void drawRoundedRect(float x, float y, float w, float h, float r, float fth, unsigned int col)
{
const unsigned n = CIRCLE_VERTS/4;
float verts[(n+1)*4*2];
const float* cverts = getCircleVerts();
float* v = verts;
for (unsigned i = 0; i <= n; ++i)
{
*v++ = x+w-r + cverts[i*2]*r;
*v++ = y+h-r + cverts[i*2+1]*r;
}
for (unsigned i = n; i <= n*2; ++i)
{
*v++ = x+r + cverts[i*2]*r;
*v++ = y+h-r + cverts[i*2+1]*r;
}
for (unsigned i = n*2; i <= n*3; ++i)
{
*v++ = x+r + cverts[i*2]*r;
*v++ = y+r + cverts[i*2+1]*r;
}
for (unsigned i = n*3; i < n*4; ++i)
{
*v++ = x+w-r + cverts[i*2]*r;
*v++ = y+r + cverts[i*2+1]*r;
}
*v++ = x+w-r + cverts[0]*r;
*v++ = y+r + cverts[1]*r;
drawPolygon(verts, (n+1)*4, fth, col);
}
static void drawLine(float x0, float y0, float x1, float y1, float r, float fth, unsigned int col)
{
float dx = x1-x0;
float dy = y1-y0;
float d = sqrtf(dx*dx+dy*dy);
if (d > 0.0001f)
{
d = 1.0f/d;
dx *= d;
dy *= d;
}
float t = dx;
dx = dy;
dy = -t;
float verts[4*2];
r -= fth;
r *= 0.5f;
if (r < 0.01f) r = 0.01f;
dx *= r;
dy *= r;
verts[0] = x0-dx;
verts[1] = y0-dy;
verts[2] = x0+dx;
verts[3] = y0+dy;
verts[4] = x1+dx;
verts[5] = y1+dy;
verts[6] = x1-dx;
verts[7] = y1-dy;
drawPolygon(verts, 4, fth, col);
}
void renderGfxCmdQueue(void (*drawText)(int x, int y, int dir, const char* text, unsigned int col))
{
glDisable(GL_SCISSOR_TEST);
for (unsigned i = 0; i < g_gfxCmdQueueSize; ++i)
{
const GfxCmd& cmd = g_gfxCmdQueue[i];
if (cmd.type == GFXCMD_RECT)
{
if (cmd.rect.r == 0)
{
drawRect((float)cmd.rect.x+0.5f, (float)cmd.rect.y+0.5f,
(float)cmd.rect.w-1, (float)cmd.rect.h-1,
1.0f, cmd.col);
}
else
{
drawRoundedRect((float)cmd.rect.x+0.5f, (float)cmd.rect.y+0.5f,
(float)cmd.rect.w-1, (float)cmd.rect.h-1,
(float)cmd.rect.r, 1.0f, cmd.col);
}
}
else if (cmd.type == GFXCMD_TRIANGLE)
{
glColor4ub(cmd.col&0xff, (cmd.col>>8)&0xff, (cmd.col>>16)&0xff, (cmd.col>>24)&0xff);
if (cmd.flags == 1)
{
const float verts[3*2] =
{
(float)cmd.rect.x+0.5f, (float)cmd.rect.y+0.5f,
(float)cmd.rect.x+0.5f+(float)cmd.rect.w-1, (float)cmd.rect.y+0.5f+(float)cmd.rect.h/2-0.5f,
(float)cmd.rect.x+0.5f, (float)cmd.rect.y+0.5f+(float)cmd.rect.h-1,
};
drawPolygon(verts, 3, 1.0f, cmd.col);
}
if (cmd.flags == 2)
{
const float verts[3*2] =
{
(float)cmd.rect.x+0.5f, (float)cmd.rect.y+(float)cmd.rect.h-1,
(float)cmd.rect.x+0.5f+(float)cmd.rect.w/2-0.5f, (float)cmd.rect.y+0.5f,
(float)cmd.rect.x+0.5f+(float)cmd.rect.w-1, (float)cmd.rect.y+0.5f+(float)cmd.rect.h-1,
};
drawPolygon(verts, 3, 1.0f, cmd.col);
}
}
else if (cmd.type == GFXCMD_TEXT)
{
drawText(cmd.text.x, cmd.text.y, cmd.text.dir, cmd.text.text, cmd.col);
}
else if (cmd.type == GFXCMD_SCISSOR)
{
if (cmd.flags)
{
glEnable(GL_SCISSOR_TEST);
glScissor(cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h);
}
else
{
glDisable(GL_SCISSOR_TEST);
}
}
}
glDisable(GL_SCISSOR_TEST);
}
void addGfxCmdScissor(int x, int y, int w, int h)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_SCISSOR;
cmd.flags = x < 0 ? 0 : 1; // on/off flag.
cmd.col = 0;
cmd.rect.x = (short)x;
cmd.rect.y = (short)y;
cmd.rect.w = (short)w;
cmd.rect.h = (short)h;
}
void addGfxCmdRect(int x, int y, int w, int h, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_RECT;
cmd.flags = 0;
cmd.col = color;
cmd.rect.x = (short)x;
cmd.rect.y = (short)y;
cmd.rect.w = (short)w;
cmd.rect.h = (short)h;
cmd.rect.r = 0;
}
void addGfxCmdRoundedRect(int x, int y, int w, int h, int r, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_RECT;
cmd.flags = 0;
cmd.col = color;
cmd.rect.x = (short)x;
cmd.rect.y = (short)y;
cmd.rect.w = (short)w;
cmd.rect.h = (short)h;
cmd.rect.r = (short)r;
}
void addGfxCmdTriangle(int x, int y, int w, int h, int flags, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_TRIANGLE;
cmd.flags = (char)flags;
cmd.col = color;
cmd.rect.x = (short)x;
cmd.rect.y = (short)y;
cmd.rect.w = (short)w;
cmd.rect.h = (short)h;
}
void addGfxCmdText(int x, int y, int dir, const char* text, unsigned int color)
{
if (g_gfxCmdQueueSize >= GFXCMD_QUEUE_SIZE)
return;
GfxCmd& cmd = g_gfxCmdQueue[g_gfxCmdQueueSize++];
cmd.type = GFXCMD_TEXT;
cmd.flags = 0;
cmd.col = color;
cmd.text.x = (short)x;
cmd.text.y = (short)y;
cmd.text.dir = (short)dir;
cmd.text.text = allocText(text);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct GuiState
{
GuiState() :
mbutPressed(false), mbutReleased(false), mbut(false), mx(-1), my(-1),
isHot(false), isActive(false), wentActive(false),
dragX(0), dragY(0), dragOrig(0),
widgetX(0), widgetY(0), widgetW(100),
active(0), hot(0), hotToBe(0)
{
}
bool mbutPressed, mbutReleased;
bool mbut;
int mx,my;
unsigned int active;
unsigned int hot;
unsigned int hotToBe;
bool isHot;
bool isActive;
bool wentActive;
int dragX, dragY;
float dragOrig;
int widgetX, widgetY, widgetW;
};
static GuiState g_state;
inline bool anyActive()
{
return g_state.active != 0;
}
inline bool isActive(unsigned int id)
{
return g_state.active == id;
}
inline bool isHot(unsigned int id)
{
return g_state.hot == id;
}
inline bool inRect(int x, int y, int w, int h)
{
return g_state.mx >= x && g_state.mx <= x+w && g_state.my >= y && g_state.my <= y+h;
}
void clearInput()
{
g_state.mbutPressed = false;
g_state.mbutReleased = false;
}
void clearActive(void)
{
g_state.active = 0;
// mark all UI for this frame as processed
clearInput();
}
void setActive(unsigned int id)
{
g_state.active = id;
g_state.wentActive = true;
}
void setHot(unsigned int id)
{
g_state.hotToBe = id;
}
bool buttonLogic(unsigned int id, bool over)
{
bool res = false;
// process down
if (!anyActive())
{
if (over)
setHot(id);
if (isHot(id) && g_state.mbutPressed)
setActive(id);
}
// if button is active, then react on left up
if (isActive(id))
{
g_state.isActive = true;
if (over)
setHot(id);
if (g_state.mbutReleased)
{
if (isHot(id))
res = true;
clearActive();
}
}
if (isHot(id))
g_state.isHot = true;
return res;
}
static void updateInput()
{
int mx, my;
Uint8 state = SDL_GetMouseState(&mx, &my);
bool mbut = (state & SDL_BUTTON_LMASK) != 0;
SDL_Surface* screen = SDL_GetVideoSurface();
my = screen->h-1 - my;
g_state.mx = mx;
g_state.my = my;
g_state.mbutPressed = !g_state.mbut && mbut;
g_state.mbutReleased = g_state.mbut && !mbut;
g_state.mbut = mbut;
}
void imguiBeginFrame()
{
updateInput();
g_state.hot = g_state.hotToBe;
g_state.hotToBe = 0;
g_state.wentActive = false;
g_state.isActive = false;
g_state.isHot = false;
g_state.widgetX = 0;
g_state.widgetY = 0;
g_state.widgetW = 0;
resetGfxCmdQueue();
}
void imguiEndFrame()
{
clearInput();
}
void imguiRender(void (*drawText)(int x, int y, int dir, const char* text, unsigned int col))
{
renderGfxCmdQueue(drawText);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const int BUTTON_HEIGHT = 20;
static const int SLIDER_HEIGHT = 20;
static const int SLIDER_MARKER_WIDTH = 10;
static const int CHECK_SIZE = 8;
static const int DEFAULT_SPACING = 4;
static const int TEXT_HEIGHT = 8;
static const int SCROLL_AREA_PADDING = 6;
static const int INTEND_SIZE = 16;
static const int AREA_HEADER = 28;
static int g_scrollTop = 0;
static int g_scrollBottom = 0;
static int g_scrollRight = 0;
static int g_scrollAreaTop = 0;
static int* g_scrollVal = 0;
static int g_focusTop = 0;
static int g_focusBottom = 0;
static unsigned int g_scrollId = 0;
bool imguiBeginScrollArea(unsigned int id, const char* name, int x, int y, int w, int h, int* scroll)
{
g_scrollId = id;
g_state.widgetX = x + SCROLL_AREA_PADDING;
g_state.widgetY = y+h-AREA_HEADER + (*scroll);
g_state.widgetW = w - SCROLL_AREA_PADDING*4;
g_scrollTop = y-AREA_HEADER+h;
g_scrollBottom = y+SCROLL_AREA_PADDING;
g_scrollRight = x+w - SCROLL_AREA_PADDING*3;
g_scrollVal = scroll;
g_scrollAreaTop = g_state.widgetY;
g_focusTop = y-AREA_HEADER;
g_focusBottom = y-AREA_HEADER+h;
addGfxCmdRoundedRect(x, y, w, h, 6, RGBA(0,0,0,192));
addGfxCmdText(x+AREA_HEADER/2, y+h-AREA_HEADER/2-TEXT_HEIGHT/2, 1, name, RGBA(255,255,255,128));
addGfxCmdScissor(x+SCROLL_AREA_PADDING, y+SCROLL_AREA_PADDING, w-SCROLL_AREA_PADDING*4, h-AREA_HEADER-SCROLL_AREA_PADDING);
return inRect(x, y, w, h);
}
void imguiEndScrollArea()
{
// Disable scissoring.
addGfxCmdScissor(-1,-1,-1,-1);
// Draw scroll bar
int x = g_scrollRight+SCROLL_AREA_PADDING/2;
int y = g_scrollBottom;
int w = SCROLL_AREA_PADDING*2;
int h = g_scrollTop - g_scrollBottom;
int stop = g_scrollAreaTop;
int sbot = g_state.widgetY;
int sh = stop - sbot; // The scrollable area height.
float barHeight = (float)h/(float)sh;
if (barHeight < 1)
{
float barY = (float)(y - sbot)/(float)sh;
if (barY < 0) barY = 0;
if (barY > 1) barY = 1;
// Handle scroll bar logic.
unsigned int hid = g_scrollId;
int hx = x;
int hy = y + (int)(barY*h);
int hw = w;
int hh = (int)(barHeight*h);
const int range = h - (hh-1);
bool over = inRect(hx, hy, hw, hh);
buttonLogic(hid, over);
if (isActive(hid))
{
float u = (float)(hy-y) / (float)range;
if (g_state.wentActive)
{
g_state.dragY = g_state.my;
g_state.dragOrig = u;
}
if (g_state.dragY != g_state.my)
{
u = g_state.dragOrig + (g_state.my - g_state.dragY) / (float)range;
if (u < 0) u = 0;
if (u > 1) u = 1;
*g_scrollVal = (int)((1-u) * (sh - h));
}
}
// BG
addGfxCmdRoundedRect(x, y, w, h, w/2-1, RGBA(0,0,0,196));
// Bar
if (isActive(hid))
addGfxCmdRoundedRect(hx, hy, hw, hh, w/2-1, RGBA(255,196,0,196));
else
addGfxCmdRoundedRect(hx, hy, hw, hh, w/2-1, isHot(hid) ? RGBA(255,196,0,96) : RGBA(255,255,255,64));
}
}
bool imguiButton(unsigned int id, const char* text)
{
int x = g_state.widgetX;
int y = g_state.widgetY - BUTTON_HEIGHT;
int w = g_state.widgetW;
int h = BUTTON_HEIGHT;
g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
bool over = inRect(x, y, w, h);
bool res = buttonLogic(id, over);
addGfxCmdRoundedRect(x, y, w, h, BUTTON_HEIGHT/2-1, RGBA(128,128,128, isActive(id)?196:96));
addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, 1, text, isHot(id) ? RGBA(255,196,0,255) : RGBA(255,255,255,200));
return res;
}
bool imguiItem(unsigned int id, const char* text)
{
int x = g_state.widgetX;
int y = g_state.widgetY - BUTTON_HEIGHT;
int w = g_state.widgetW;
int h = BUTTON_HEIGHT;
g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
bool over = inRect(x, y, w, h);
bool res = buttonLogic(id, over);
if (isHot(id))
addGfxCmdRoundedRect(x, y, w, h, 2, RGBA(255,196,0,isActive(id)?196:96));
addGfxCmdText(x+BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, 1, text, RGBA(255,255,255,200));
return res;
}
bool imguiCheck(unsigned int id, const char* text, bool checked)
{
int x = g_state.widgetX;
int y = g_state.widgetY - BUTTON_HEIGHT;
int w = g_state.widgetW;
int h = BUTTON_HEIGHT;
g_state.widgetY -= BUTTON_HEIGHT + DEFAULT_SPACING;
bool over = inRect(x, y, w, h);
bool res = buttonLogic(id, over);
const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2;
const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2;
addGfxCmdRoundedRect(cx-3, cy-3, CHECK_SIZE+6, CHECK_SIZE+6, 4, RGBA(128,128,128, isActive(id)?196:96));
if (checked)
addGfxCmdRoundedRect(cx, cy, CHECK_SIZE, CHECK_SIZE, CHECK_SIZE/2-1, RGBA(255,255,255,isActive(id)?255:200));
addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, 1, text, isHot(id) ? RGBA(255,196,0,255) : RGBA(255,255,255,200));
return res;
}
bool imguiCollapse(unsigned int id, const char* text, bool checked)
{
int x = g_state.widgetX;
int y = g_state.widgetY - BUTTON_HEIGHT;
int w = g_state.widgetW;
int h = BUTTON_HEIGHT;
g_state.widgetY -= BUTTON_HEIGHT; // + DEFAULT_SPACING;
const int cx = x+BUTTON_HEIGHT/2-CHECK_SIZE/2;
const int cy = y+BUTTON_HEIGHT/2-CHECK_SIZE/2;
bool over = inRect(x, y, w, h);
bool res = buttonLogic(id, over);
if (checked)
addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 1, RGBA(255,255,255,isActive(id)?255:200));
else
addGfxCmdTriangle(cx, cy, CHECK_SIZE, CHECK_SIZE, 2, RGBA(255,255,255,isActive(id)?255:200));
addGfxCmdText(x+BUTTON_HEIGHT, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, 1, text, isHot(id) ? RGBA(255,196,0,255) : RGBA(255,255,255,200));
return res;
}
void imguiLabel(unsigned int /*id*/, const char* text)
{
int x = g_state.widgetX;
int y = g_state.widgetY - BUTTON_HEIGHT;
g_state.widgetY -= BUTTON_HEIGHT;
addGfxCmdText(x, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, 1, text, RGBA(255,255,255,255));
}
void imguiValue(unsigned int /*id*/, const char* text)
{
const int x = g_state.widgetX;
const int y = g_state.widgetY - BUTTON_HEIGHT;
const int w = g_state.widgetW;
g_state.widgetY -= BUTTON_HEIGHT;
addGfxCmdText(x+w-BUTTON_HEIGHT/2, y+BUTTON_HEIGHT/2-TEXT_HEIGHT/2, -1, text, RGBA(255,255,255,200));
}
bool imguiSlider(unsigned int id, const char* text, float* val, float vmin, float vmax, float vinc)
{
int x = g_state.widgetX;
int y = g_state.widgetY - BUTTON_HEIGHT;
int w = g_state.widgetW;
int h = SLIDER_HEIGHT;
g_state.widgetY -= SLIDER_HEIGHT + DEFAULT_SPACING;
addGfxCmdRoundedRect(x, y, w, h, 4, RGBA(0,0,0,128));
const int range = w - SLIDER_MARKER_WIDTH;
float u = (*val - vmin) / (vmax-vmin);
if (u < 0) u = 0;
if (u > 1) u = 1;
int m = (int)(u * range);
bool over = inRect(x+m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT);
bool res = buttonLogic(id, over);
bool valChanged = false;
if (isActive(id))
{
if (g_state.wentActive)
{
g_state.dragX = g_state.mx;
g_state.dragOrig = u;
}
if (g_state.dragX != g_state.mx)
{
u = g_state.dragOrig + (float)(g_state.mx - g_state.dragX) / (float)range;
if (u < 0) u = 0;
if (u > 1) u = 1;
*val = vmin + u*(vmax-vmin);
*val = floorf(*val / vinc)*vinc; // Snap to vinc
m = (int)(u * range);
valChanged = true;
}
}
if (isActive(id))
addGfxCmdRoundedRect(x+m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT, 4, RGBA(255,255,255,255));
else
addGfxCmdRoundedRect(x+m, y, SLIDER_MARKER_WIDTH, SLIDER_HEIGHT, 4, isHot(id) ? RGBA(255,196,0,128) : RGBA(255,255,255,64));
int digits = (int)(ceilf(log10f(vinc)));
char fmt[16];
snprintf(fmt, 16, "%%.%df", digits >= 0 ? 0 : -digits);
char msg[128];
snprintf(msg, 128, fmt, *val);
addGfxCmdText(x+SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, 1, text, isHot(id) ? RGBA(255,196,0,255) : RGBA(255,255,255,200));
addGfxCmdText(x+w-SLIDER_HEIGHT/2, y+SLIDER_HEIGHT/2-TEXT_HEIGHT/2, -1, msg, isHot(id) ? RGBA(255,196,0,255) : RGBA(255,255,255,200));
return res || valChanged;
}
void imguiIndent()
{
g_state.widgetX += INTEND_SIZE;
g_state.widgetW -= INTEND_SIZE;
}
void imguiUnindent()
{
g_state.widgetX -= INTEND_SIZE;
g_state.widgetW += INTEND_SIZE;
}
void imguiSeparator()
{
g_state.widgetY -= DEFAULT_SPACING*3;
}

45
Recast/Examples/imgui.h Normal file
View File

@ -0,0 +1,45 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef IMGUI_H
#define IMGUI_H
#define GENID ((__LINE__) << 16)
#define GENID1(x) ((__LINE__) << 16 | (x))
void imguiBeginFrame();
void imguiEndFrame();
void imguiRender(void (*drawText)(int x, int y, int dir, const char* text, unsigned int col));
bool imguiBeginScrollArea(unsigned int id, const char* name, int x, int y, int w, int h, int* scroll);
void imguiEndScrollArea();
void imguiIndent();
void imguiUnindent();
void imguiSeparator();
bool imguiButton(unsigned int id, const char* text);
bool imguiItem(unsigned int id, const char* text);
bool imguiCheck(unsigned int id, const char* text, bool checked);
bool imguiCollapse(unsigned int id, const char* text, bool checked);
void imguiLabel(unsigned int id, const char* text);
void imguiValue(unsigned int id, const char* text);
bool imguiSlider(unsigned int id, const char* text, float* val, float vmin, float vmax, float vinc);
#endif // IMGUI_H

390
Recast/Include/Recast.h Normal file
View File

@ -0,0 +1,390 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef RECAST_H
#define RECAST_H
struct rcConfig
{
int width, height; // Dimensions of the rasterized heighfield
float cs, ch; // Grid cell size and height.
float bmin[3], bmax[3]; // Grid bounds.
float walkableSlopeAngle; // Maximum walkble slope angle in degrees.
int walkableHeight; // Minimum height where the agent can still walk.
int walkableClimb; // Maximum height between grid cells the agent can climb.
int walkableRadius; // Radius of the agent in cells.
int maxEdgeLen; // Maximum contour edge length in cells.
float maxSimplificationError; // Maximum distance error from contour to cells.
int minRegionSize; // Minimum regions size. Smaller regions will be deleted.
int mergeRegionSize; // Minimum regions size. Smaller regions will be merged.
int maxVertsPerPoly; // Max number of vertices per polygon.
};
struct rcSpan
{
unsigned int smin : 15; // Span min height.
unsigned int smax : 15; // Span max height.
unsigned int flags : 2; // Span flags.
rcSpan* next;
};
struct rcSpanPool
{
rcSpanPool* next;
rcSpan items[1];
};
struct rcHeightfield
{
inline rcHeightfield() : width(0), height(0), spans(0), pools(0), freelist(0) {}
inline ~rcHeightfield()
{
delete [] spans;
while (pools)
{
rcSpanPool* next = pools->next;
delete [] reinterpret_cast<unsigned char*>(pools);
pools = next;
}
}
int width, height;
rcSpan** spans;
rcSpanPool* pools;
rcSpan* freelist;
};
struct rcCompactCell
{
unsigned int index : 24;
unsigned int count : 8;
};
struct rcCompactSpan
{
unsigned short y;
unsigned short reg;
unsigned short dist;
unsigned short con;
unsigned char h;
unsigned char flags;
};
struct rcCompactHeightfield
{
inline rcCompactHeightfield() : cells(0), spans(0), maxDistance(0), maxRegions(0) {}
inline ~rcCompactHeightfield() { delete [] cells; delete [] spans; }
int width, height;
int spanCount;
int walkableHeight, walkableClimb;
unsigned short maxDistance;
unsigned short maxRegions;
float minx, miny, minz;
float maxx, maxy, maxz;
float cs, ch;
rcCompactCell* cells;
rcCompactSpan* spans;
};
struct rcContour
{
inline rcContour() : verts(0), nverts(0), rverts(0), nrverts(0), cx(0), cy(0), cz(0) { }
inline ~rcContour() { delete [] verts; delete [] rverts; }
int* verts;
int nverts;
int* rverts;
int nrverts;
int cx,cy,cz;
unsigned short reg;
};
struct rcContourSet
{
inline rcContourSet() : conts(0), nconts(0) {}
inline ~rcContourSet() { delete [] conts; }
rcContour* conts;
int nconts;
};
struct rcPolyMesh
{
inline rcPolyMesh() : verts(0), polys(0), nverts(0), npolys(0), nvp(3) {}
inline ~rcPolyMesh() { delete [] verts; delete [] polys; }
unsigned short* verts;
unsigned short* polys;
int nverts;
int npolys;
int nvp;
};
class rcIntArray
{
int* m_data;
int m_size, m_cap;
public:
inline rcIntArray() : m_data(0), m_size(0), m_cap(0) {}
inline rcIntArray(int n) : m_data(0), m_size(0), m_cap(n) { m_data = new int[n]; }
inline ~rcIntArray() { delete [] m_data; }
void resize(int n);
inline void push(int item) { resize(m_size+1); m_data[m_size-1] = item; }
inline int pop() { if (m_size > 0) m_size--; return m_data[m_size]; }
inline const int& operator[](int i) const { return m_data[i]; }
inline int& operator[](int i) { return m_data[i]; }
inline int size() const { return m_size; }
};
enum rcSpanFlags
{
RC_WALKABLE = 0x01,
RC_REACHABLE = 0x02,
};
// Comppact span neighbour helpers.
inline int rcGetCon(const rcCompactSpan& s, int dir)
{
return (s.con >> (dir*4)) & 0xf;
}
inline int rcGetDirOffsetX(int dir)
{
const int offset[4] = { -1, 0, 1, 0, };
return offset[dir&0x03];
}
inline int rcGetDirOffsetY(int dir)
{
const int offset[4] = { 0, 1, 0, -1 };
return offset[dir&0x03];
}
// Common helper functions
template<class T> inline void rcSwap(T& a, T& b) { T t = a; a = b; b = t; }
template<class T> inline T rcMin(T a, T b) { return a < b ? a : b; }
template<class T> inline T rcMax(T a, T b) { return a > b ? a : b; }
template<class T> inline T rcAbs(T a) { return a < 0 ? -a : a; }
template<class T> inline T rcSqr(T a) { return a*a; }
template<class T> inline T rcClamp(T v, T mn, T mx) { return v < mn ? mn : (v > mx ? mx : v); }
// Common vector helper functions.
inline void vcross(float* dest, const float* v1, const float* v2)
{
dest[0] = v1[1]*v2[2] - v1[2]*v2[1];
dest[1] = v1[2]*v2[0] - v1[0]*v2[2];
dest[2] = v1[0]*v2[1] - v1[1]*v2[0];
}
inline float vdot(const float* v1, const float* v2)
{
return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
inline void vsub(float* dest, const float* v1, const float* v2)
{
dest[0] = v1[0]-v2[0];
dest[1] = v1[1]-v2[1];
dest[2] = v1[2]-v2[2];
}
inline void vmin(float* mn, const float* v)
{
mn[0] = rcMin(mn[0], v[0]);
mn[1] = rcMin(mn[1], v[1]);
mn[2] = rcMin(mn[2], v[2]);
}
inline void vmax(float* mx, const float* v)
{
mx[0] = rcMax(mx[0], v[0]);
mx[1] = rcMax(mx[1], v[1]);
mx[2] = rcMax(mx[2], v[2]);
}
inline void vcopy(float* dest, const float* v)
{
dest[0] = v[0];
dest[1] = v[1];
dest[2] = v[2];
}
inline float vdistSqr(const float* v1, const float* v2)
{
float dx = v2[0] - v1[0];
float dy = v2[1] - v1[1];
float dz = v2[2] - v1[2];
return dx*dx + dy*dy + dz*dz;
}
inline void vnormalize(float* v)
{
float d = 1.0f / sqrtf(rcSqr(v[0]) + rcSqr(v[1]) + rcSqr(v[2]));
v[0] *= d;
v[1] *= d;
v[2] *= d;
}
inline bool vequal(const float* p0, const float* p1)
{
static const float thr = rcSqr(1.0f/16384.0f);
const float d = vdistSqr(p0, p1);
return d < thr;
}
// Calculated bounding box of array of vertices.
// Params:
// verts - (in) array of vertices
// nv - (in) vertex count
// bmin, bmax - (out) bounding box
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax);
// Calculates grid size based on bounding box and grid cell size.
// Params:
// bmin, bmax - (in) bounding box
// cs - (in) grid cell size
// w - (out) grid width
// h - (out) grid height
void rcCalcGridSize(float* bmin, float* bmax, float cs, int* w, int* h);
// Creates and initializes new heightfield.
// Params:
// hf - (in/out) heightfield to initialize.
// width - (in) width of the heightfield.
// height - (in) height of the heightfield.
bool rcCreateHeightfield(rcHeightfield& hf, int width, int height);
// Sets the WALKABLE flag for every triangle whose slope is below
// the maximun walkable slope angle.
// Params:
// walkableSlopeAngle - (in) maximun slope angle in degrees.
// tris - (in) array of triangle vertex indices
// norms - (in) array of triangle normals
// nt - (in) triangle count
// flags - (out) array of triangle flags
void rcMarkWalkableTriangles(const float walkableSlopeAngle,
const int* tris, const float* norms, int nt,
unsigned char* flags);
// Rasterizes the triangles into heightfield spans.
// Params:
// bmin, bmax - (in) bounding box of the heightfield
// cs - (in) grid cell size
// ch - (in) grid cell height
// verts - (in) array of vertices
// nv - (in) vertex count
// tris - (in) array of triangle vertex indices
// norms - (in) array of triangle normals
// flags - (in) array of triangle flags (uses WALKABLE)
// nt - (in) triangle count
// solid - (in) heighfield where the triangles are rasterized
void rcRasterizeTriangles(const float* bmin, const float* bmax,
float cs, float ch,
const float* verts, int nv,
const int* tris, const unsigned char* flags, int nt,
rcHeightfield& solid);
void rcFilterWalkableBorderSpans(const int walkableHeight,
const int walkableClimb,
rcHeightfield& solid);
// Removes WALKABLE flag from all spans which have smaller than
// 'walkableHeight' clearane above them.
// Params:
// walkableHeight - (in) minimum height where the agent can still walk
// solid - (in/out) heightfield describing the solid space
void rcFilterWalkableLowHeightSpans(int walkableHeight,
rcHeightfield& solid);
// Marks spans which are reachable from any of the topmost spans.
// Params:
// walkableHeight - (in) minimum height where the agent can still walk
// walkableClimb - (in) maximum height between grid cells the agent can climb
// solid - (in/out) heightfield describing the solid space
// Returns false if operation ran out of memory.
bool rcMarkReachableSpans(const int walkableHeight,
const int walkableClimb,
rcHeightfield& solid);
// Builds compact representation of the heightfield.
// Params:
// bmin, bmax - (in) bounding box of the heightfield
// cs - (in) grid cell size
// ch - (in) grid cell height
// walkableHeight - (in) minimum height where the agent can still walk
// walkableClimb - (in) maximum height between grid cells the agent can climb
// hf - (in) heightfield to be compacted
// chf - (out) compact heightfield representing the open space.
// Returns false if operation ran out of memory.
bool rcBuildCompactHeightfield(const float* bmin, const float* bmax,
const float cs, const float ch,
const int walkableHeight, const int walkableClimb,
unsigned char flags,
rcHeightfield& hf,
rcCompactHeightfield& chf);
// Builds distance field and stores it into the combat heightfield.
// Params:
// chf - (in/out) compact heightfield representing the open space.
// Returns false if operation ran out of memory.
bool rcBuildDistanceField(rcCompactHeightfield& chf);
// Divides the walkable heighfied into simple regions.
// Each region has only one contour and no overlaps.
// The regions are stored in the compact heightfield 'reg' field.
// The regions will be shrinked by the radius of the agent.
// The process sometimes creates small regions. The parameter
// 'minRegionSize' specifies the smallest allowed regions size.
// If the area of a regions is smaller than allowed, the regions is
// removed or merged to neighbour region.
// Params:
// chf - (in/out) compact heightfield representing the open space.
// walkableRadius - (in) the radius of the agent.
// minRegionSize - (in) the smallest allowed regions size.
// maxMergeRegionSize - (in) the largest allowed regions size which can be merged.
// Returns false if operation ran out of memory.
bool rcBuildRegions(rcCompactHeightfield& chf,
int walkableRadius, int minRegionSize, int mergeRegionSize);
// Builds simplified contours from the regions outlines.
// Params:
// chf - (in) compact heightfield which has regions set.
// maxError - (in) maximum allowed distance between simplified countour and cells.
// maxEdgeLen - (in) maximum allowed contour edge length in cells.
// cset - (out) Resulting contour set.
// Returns false if operation ran out of memory.
bool rcBuildContours(rcCompactHeightfield& chf,
float maxError, int maxEdgeLen,
rcContourSet& cset);
// Builds connected convex polygon mesh from contour polygons.
// Params:
// cset - (in) contour set.
// mesh - (out) poly mesh.
// nvp - (int) maximum number of vertices per polygon.
// Returns false if operation ran out of memory.
bool rcBuildPolyMesh(rcContourSet& cset, rcPolyMesh& mesh, int nvp);
bool rcBuildNavMesh(const rcConfig& cfg,
const float* verts, const int nverts,
const int* tris, const unsigned char* tflags, const int ntris,
rcHeightfield& solid,
rcCompactHeightfield& chf,
rcContourSet& cset,
rcPolyMesh& polyMesh);
#endif // RECAST_H

View File

@ -0,0 +1,59 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef RECAST_DEBUGDRAW_H
#define RECAST_DEBUGDRAW_H
inline int bit(int a, int b)
{
return (a & (1 << b)) >> b;
}
inline void intToCol(int i, float* col)
{
int r = bit(i, 0) + bit(i, 3) * 2 + 1;
int g = bit(i, 1) + bit(i, 4) * 2 + 1;
int b = bit(i, 2) + bit(i, 5) * 2 + 1;
col[0] = 1 - r*63.0f/255.0f;
col[1] = 1 - g*63.0f/255.0f;
col[2] = 1 - b*63.0f/255.0f;
}
void rcDebugDrawHeightfieldSolid(const struct rcHeightfield& hf,
const float* orig, float cs, float ch);
void rcDebugDrawHeightfieldWalkable(const struct rcHeightfield& hf,
const float* orig, float cs, float ch);
void rcDebugDrawMesh(const class rcMeshLoaderObj& mesh, const unsigned char* flags);
void rcDebugDrawCompactHeightfieldSolid(const struct rcCompactHeightfield& chf);
void rcDebugDrawCompactHeightfieldRegions(const struct rcCompactHeightfield& chf);
void rcDebugDrawCompactHeightfieldDistance(const struct rcCompactHeightfield& chf);
void rcDebugDrawRawContours(const struct rcContourSet& cset, const float* orig, float cs, float ch);
void rcDebugDrawContours(const struct rcContourSet& cset, const float* orig, float cs, float ch);
void rcDebugDrawPolyMesh(const struct rcPolyMesh& mesh, const float* orig, float cs, float ch);
void rcDebugDrawCylinderWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col);
void rcDebugDrawBoxWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col);
void rcDebugDrawBox(float minx, float miny, float minz, float maxx, float maxy, float maxz,
const float* col1, const float* col2);
#endif // RECAST_DEBUGDRAW_H

View File

@ -0,0 +1,53 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef RECAST_LOG_H
#define RECAST_LOG_H
enum rcLogCategory
{
RC_LOG_PROGRESS = 1,
RC_LOG_WARNING,
RC_LOG_ERROR,
};
class rcLog
{
public:
rcLog();
~rcLog();
void log(rcLogCategory category, const char* format, ...);
inline void clear() { m_messageCount = 0; m_textPoolSize = 0; }
inline int getMessageCount() const { return m_messageCount; }
inline char getMessageType(int i) const { return *m_messages[i]; }
inline const char* getMessageText(int i) const { return m_messages[i]+1; }
private:
static const int MAX_MESSAGES = 1000;
const char* m_messages[MAX_MESSAGES];
int m_messageCount;
static const int TEXT_POOL_SIZE = 8000;
char m_textPool[TEXT_POOL_SIZE];
int m_textPoolSize;
};
void rcSetLog(rcLog* log);
rcLog* rcGetLog();
#endif // RECAST_LOG_H

View File

@ -0,0 +1,38 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#ifndef RECAST_TIMER_H
#define RECAST_TIMER_H
#ifdef WIN32
//#include <stdint.h>
typedef __int64 rcTimeVal;
rcTimeVal rcGetPerformanceTimer();
int rcGetDeltaTimeUsec(rcTimeVal start, rcTimeVal end);
#else
// OSX
#include <stdint.h>
typedef uint64_t rcTimeVal;
rcTimeVal rcGetPerformanceTimer();
int rcGetDeltaTimeUsec(rcTimeVal start, rcTimeVal end);
#endif
#endif // RECAST_TIMER_H

18
Recast/License.txt Normal file
View File

@ -0,0 +1,18 @@
Copyright (c) 2009 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

13
Recast/Readme.txt Normal file
View File

@ -0,0 +1,13 @@
Detour Version 1.0
Welcome to Detour!
Detour is a navigation system for games. The system comes with two parts: 1) Automatic preprocess which generates navmesh automatically from a polygon soup and, 2) runtime library which allows to do spatic queries and pathfinding on the navmesh.
The project files with this distribution with with Microsoft Visual C++ 2008 (you can download it for free) and XCode 3.1.
You can find examples how to use the preprocess and runtime on the Examples directory.
Mikko Mononen
memon@inside.org

302
Recast/Source/Recast.cpp Normal file
View File

@ -0,0 +1,302 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include <float.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "Recast.h"
#include "RecastLog.h"
#include "RecastTimer.h"
void rcIntArray::resize(int n)
{
if (n > m_cap)
{
if (!m_cap) m_cap = 8;
while (m_cap < n) m_cap *= 2;
int* newData = new int[m_cap];
if (m_size && newData) memcpy(newData, m_data, m_size*sizeof(int));
delete [] m_data;
m_data = newData;
}
m_size = n;
}
void rcCalcBounds(const float* verts, int nv, float* bmin, float* bmax)
{
// Calculate bounding box.
vcopy(bmin, verts);
vcopy(bmax, verts);
for (int i = 1; i < nv; ++i)
{
const float* v = &verts[i*3];
vmin(bmin, v);
vmax(bmax, v);
}
}
void rcCalcGridSize(float* bmin, float* bmax, float cs, int* w, int* h)
{
*w = (int)((bmax[0] - bmin[0])/cs+0.5f);
*h = (int)((bmax[2] - bmin[2])/cs+0.5f);
}
bool rcCreateHeightfield(rcHeightfield& hf, int width, int height)
{
hf.width = width;
hf.height = height;
hf.spans = new rcSpan*[hf.width*hf.height];
if (!hf.spans)
return false;
memset(hf.spans, 0, sizeof(rcSpan*)*hf.width*hf.height);
return true;
}
void rcMarkWalkableTriangles(const float walkableSlopeAngle,
const int* tris, const float* norms, int nt,
unsigned char* flags)
{
const float walkableThr = cosf(walkableSlopeAngle/180.0f*(float)M_PI);
for (int i = 0; i < nt; ++i)
{
// Check if the face is walkable.
if (norms[i*3+1] > walkableThr)
flags[i] |= RC_WALKABLE;
}
}
static int getSpanCount(unsigned char flags, rcHeightfield& hf)
{
const int w = hf.width;
const int h = hf.height;
int spanCount = 0;
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
for (rcSpan* s = hf.spans[x + y*w]; s; s = s->next)
{
if (s->flags == flags)
spanCount++;
}
}
}
return spanCount;
}
inline void setCon(rcCompactSpan& s, int dir, int i)
{
s.con &= ~(0xf << (dir*4));
s.con |= (i&0xf) << (dir*4);
}
bool rcBuildCompactHeightfield(const float* bmin, const float* bmax,
const float cs, const float ch,
const int walkableHeight, const int walkableClimb,
unsigned char flags, rcHeightfield& hf,
rcCompactHeightfield& chf)
{
rcTimeVal startTime = rcGetPerformanceTimer();
const int w = hf.width;
const int h = hf.height;
const int spanCount = getSpanCount(flags, hf);
// Fill in header.
chf.width = w;
chf.height = h;
chf.spanCount = spanCount;
chf.walkableHeight = walkableHeight;
chf.walkableClimb = walkableClimb;
chf.maxRegions = 0;
chf.minx = bmin[0];
chf.miny = bmin[1];
chf.minz = bmin[2];
chf.maxx = bmax[0];
chf.maxy = bmax[1] + walkableHeight*ch;
chf.maxz = bmax[2];
chf.cs = cs;
chf.ch = ch;
chf.cells = new rcCompactCell[w*h];
if (!chf.cells)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.cells' (%d)", w*h);
return false;
}
memset(chf.cells, 0, sizeof(rcCompactCell)*w*h);
chf.spans = new rcCompactSpan[spanCount];
if (!chf.spans)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildCompactHeightfield: Out of memory 'chf.spans' (%d)", spanCount);
return false;
}
memset(chf.spans, 0, sizeof(rcCompactSpan)*spanCount);
const int MAX_HEIGHT = 0xffff;
// Fill in cells and spans.
int idx = 0;
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
const rcSpan* s = hf.spans[x + y*w];
// If there are no spans at this cell, just leave the data to index=0, count=0.
if (!s) continue;
rcCompactCell& c = chf.cells[x+y*w];
c.index = idx;
c.count = 0;
while (s)
{
if (s->flags == flags)
{
const int bot = (int)s->smax;
const int top = (int)s->next ? (int)s->next->smin : MAX_HEIGHT;
chf.spans[idx].y = (unsigned short)rcClamp(bot, 0, 0xffff);
chf.spans[idx].h = (unsigned char)rcClamp(top - bot, 0, 0xff);
idx++;
c.count++;
}
s = s->next;
}
}
}
// Find neighbour connections.
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
const rcCompactCell& c = chf.cells[x+y*w];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
rcCompactSpan& s = chf.spans[i];
for (int dir = 0; dir < 4; ++dir)
{
setCon(s, dir, 0xf);
const int nx = x + rcGetDirOffsetX(dir);
const int ny = y + rcGetDirOffsetY(dir);
// First check that the neighbour cell is in bounds.
if (nx < 0 || ny < 0 || nx >= w || ny >= h)
continue;
// Iterate over all neighbour spans and check if any of the is
// accessible from current cell.
const rcCompactCell& nc = chf.cells[nx+ny*w];
for (int k = (int)nc.index, nk = (int)(nc.index+nc.count); k < nk; ++k)
{
const rcCompactSpan& ns = chf.spans[k];
const int bot = rcMax(s.y, ns.y);
const int top = rcMin(s.y+s.h, ns.y+ns.h);
// Check that the gap between the spans is walkable,
// and that the climb height between the gaps is not too high.
if ((top - bot) >= walkableHeight && rcAbs((int)ns.y - (int)s.y) <= walkableClimb)
{
// Mark direction as walkable.
setCon(s, dir, k - (int)nc.index);
break;
}
}
}
}
}
}
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetLog())
rcGetLog()->log(RC_LOG_PROGRESS, "Build compact: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
return true;
}
bool rcBuildNavMesh(const rcConfig& cfg,
const float* verts, const int nverts,
const int* tris, const unsigned char* tflags, const int ntris,
rcHeightfield& solid,
rcCompactHeightfield& chf,
rcContourSet& cset,
rcPolyMesh& polyMesh)
{
if (!rcCreateHeightfield(solid, cfg.width, cfg.height))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildNavMesh: Could not create solid heightfield.");
return false;
}
rcRasterizeTriangles(cfg.bmin, cfg.bmax, cfg.cs, cfg.ch,
verts, nverts, tris, tflags, ntris, solid);
rcFilterWalkableBorderSpans(cfg.walkableHeight, cfg.walkableClimb, solid);
rcFilterWalkableLowHeightSpans(cfg.walkableHeight, solid);
if (!rcMarkReachableSpans(cfg.walkableHeight, cfg.walkableClimb, solid))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildNavMesh: Could not build navigable heightfield.");
return false;
}
if (!rcBuildCompactHeightfield(cfg.bmin, cfg.bmax, cfg.cs, cfg.ch,
cfg.walkableHeight, cfg.walkableClimb,
RC_WALKABLE|RC_REACHABLE, solid, chf))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildNavMesh: Could not build compact data.");
return false;
}
if (!rcBuildDistanceField(chf))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildNavMesh: Could not build distance fields.");
return false;
}
if (!rcBuildRegions(chf, cfg.walkableRadius, cfg.minRegionSize, cfg.mergeRegionSize))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildNavMesh: Could not build regions.");
return false;
}
if (!rcBuildContours(chf, cfg.maxSimplificationError, cfg.maxEdgeLen, cset))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildNavMesh: Could not create contours.");
return false;
}
if (!rcBuildPolyMesh(cset, polyMesh, cfg.maxVertsPerPoly))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildNavMesh: Could not triangulate contours.");
return false;
}
return true;
}

View File

@ -0,0 +1,665 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "Recast.h"
#include "RecastLog.h"
#include "RecastTimer.h"
static int getCornerHeight(int x, int y, int i, int dir,
const rcCompactHeightfield& chf)
{
const rcCompactSpan& s = chf.spans[i];
int ch = (int)s.y;
int dirp = (dir+1) & 0x3;
if (rcGetCon(s, dir) != 0xf)
{
const int ax = x + rcGetDirOffsetX(dir);
const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai];
ch = rcMax(ch, (int)as.y);
if (rcGetCon(as, dirp) != 0xf)
{
const int ax2 = ax + rcGetDirOffsetX(dirp);
const int ay2 = ay + rcGetDirOffsetY(dirp);
const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dirp);
const rcCompactSpan& as2 = chf.spans[ai2];
ch = rcMax(ch, (int)as2.y);
}
}
if (rcGetCon(s, dirp) != 0xf)
{
const int ax = x + rcGetDirOffsetX(dirp);
const int ay = y + rcGetDirOffsetY(dirp);
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dirp);
const rcCompactSpan& as = chf.spans[ai];
ch = rcMax(ch, (int)as.y);
if (rcGetCon(as, dir) != 0xf)
{
const int ax2 = ax + rcGetDirOffsetX(dir);
const int ay2 = ay + rcGetDirOffsetY(dir);
const int ai2 = (int)chf.cells[ax2+ay2*chf.width].index + rcGetCon(as, dir);
const rcCompactSpan& as2 = chf.spans[ai2];
ch = rcMax(ch, (int)as2.y);
}
}
return ch;
}
static void walkContour(int x, int y, int i,
rcCompactHeightfield& chf,
unsigned char* flags, rcIntArray& points)
{
// Choose the first non-connected edge
unsigned char dir = 0;
while ((flags[i] & (1 << dir)) == 0)
dir++;
unsigned char startDir = dir;
int starti = i;
int iter = 0;
while (++iter < 40000)
{
if (flags[i] & (1 << dir))
{
// Choose the edge corner
int px = x;
int py = getCornerHeight(x, y, i, dir, chf);
int pz = y;
switch(dir)
{
case 0: pz++; break;
case 1: px++; pz++; break;
case 2: px++; break;
}
int r = 0;
const rcCompactSpan& s = chf.spans[i];
if (rcGetCon(s, dir) != 0xf)
{
const int ax = x + rcGetDirOffsetX(dir);
const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*chf.width].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai];
r = (int)as.reg;
}
points.push(px);
points.push(py);
points.push(pz);
points.push(r);
flags[i] &= ~(1 << dir); // Remove visited edges
dir = (dir+1) & 0x3; // Rotate CW
}
else
{
int ni = -1;
const int nx = x + rcGetDirOffsetX(dir);
const int ny = y + rcGetDirOffsetY(dir);
const rcCompactSpan& s = chf.spans[i];
if (rcGetCon(s, dir) != 0xf)
{
const rcCompactCell& nc = chf.cells[nx+ny*chf.width];
ni = (int)nc.index + rcGetCon(s, dir);
}
if (ni == -1)
{
// Should not happen.
return;
}
x = nx;
y = ny;
i = ni;
dir = (dir+3) & 0x3; // Rotate CCW
}
if (starti == i && startDir == dir)
{
break;
}
}
}
static float distancePtSeg(int x, int y, int z,
int px, int py, int pz,
int qx, int qy, int qz)
{
float pqx = (float)(qx - px);
float pqy = (float)(qy - py);
float pqz = (float)(qz - pz);
float dx = (float)(x - px);
float dy = (float)(y - py);
float dz = (float)(z - pz);
float d = pqx*pqx + pqy*pqy + pqz*pqz;
float t = pqx*dx + pqy*dy + pqz*dz;
if (d > 0)
t /= d;
if (t < 0)
t = 0;
else if (t > 1)
t = 1;
dx = px + t*pqx - x;
dy = py + t*pqy - y;
dz = pz + t*pqz - z;
return dx*dx + dy*dy + dz*dz;
}
static void simplifyContour(rcIntArray& points, rcIntArray& simplified, float maxError, int maxEdgeLen)
{
// Add initial points.
bool noConnections = true;
for (int i = 0; i < points.size(); i += 4)
{
if (points[i+3] != 0)
{
noConnections = false;
break;
}
}
if (noConnections)
{
// If there is no connections at all,
// create some initial points for the simplification process.
// Find lower-left and upper-right vertices of the contour.
int llx = points[0];
int lly = points[1];
int llz = points[2];
int lli = 0;
int urx = points[0];
int ury = points[1];
int urz = points[2];
int uri = 0;
for (int i = 0; i < points.size(); i += 4)
{
int x = points[i+0];
int y = points[i+1];
int z = points[i+2];
if (x < llx || (x == llx && z < llz))
{
llx = x;
lly = y;
llz = z;
lli = i/4;
}
if (x >= urx || (x == urx && z > urz))
{
urx = x;
ury = y;
urz = z;
uri = i/4;
}
}
simplified.push(llx);
simplified.push(lly);
simplified.push(llz);
simplified.push(lli);
simplified.push(urx);
simplified.push(ury);
simplified.push(urz);
simplified.push(uri);
}
else
{
// The contour has some portals to other regions.
// Add a new point to every location where the region changes.
for (int i = 0, ni = points.size()/4; i < ni; ++i)
{
int ii = (i+1) % ni;
if (points[i*4+3] != points[ii*4+3])
{
simplified.push(points[i*4+0]);
simplified.push(points[i*4+1]);
simplified.push(points[i*4+2]);
simplified.push(i);
}
}
}
// Add points until all raw points are within
// error tolerance to the simplified shape.
const int pn = points.size()/4;
for (int i = 0; i < simplified.size()/4; )
{
int ii = (i+1) % (simplified.size()/4);
int ax = simplified[i*4+0];
int ay = simplified[i*4+1];
int az = simplified[i*4+2];
int ai = simplified[i*4+3];
int bx = simplified[ii*4+0];
int by = simplified[ii*4+1];
int bz = simplified[ii*4+2];
int bi = simplified[ii*4+3];
// Find maximum deviation from the segment.
float maxd = 0;
int maxi = -1;
int ci = (ai+1) % pn;
// Tesselate only outer edges.
if (points[ci*4+3] == 0)
{
while (ci != bi)
{
float d = distancePtSeg(points[ci*4+0], points[ci*4+1]/4, points[ci*4+2],
ax, ay/4, az, bx, by/4, bz);
if (d > maxd)
{
maxd = d;
maxi = ci;
}
ci = (ci+1) % pn;
}
}
// If the max deviation is larger than accepted error,
// add new point, else continue to next segment.
if (maxi != -1 && maxd > (maxError*maxError))
{
// Add space for the new point.
simplified.resize(simplified.size()+4);
int n = simplified.size()/4;
for (int j = n-1; j > i; --j)
{
simplified[j*4+0] = simplified[(j-1)*4+0];
simplified[j*4+1] = simplified[(j-1)*4+1];
simplified[j*4+2] = simplified[(j-1)*4+2];
simplified[j*4+3] = simplified[(j-1)*4+3];
}
// Add the point.
simplified[(i+1)*4+0] = points[maxi*4+0];
simplified[(i+1)*4+1] = points[maxi*4+1];
simplified[(i+1)*4+2] = points[maxi*4+2];
simplified[(i+1)*4+3] = maxi;
}
else
{
++i;
}
}
// Split too long edges.
if (maxEdgeLen > 0)
{
for (int i = 0; i < simplified.size()/4; )
{
int ii = (i+1) % (simplified.size()/4);
int ax = simplified[i*4+0];
int az = simplified[i*4+2];
int ai = simplified[i*4+3];
int bx = simplified[ii*4+0];
int bz = simplified[ii*4+2];
int bi = simplified[ii*4+3];
// Find maximum deviation from the segment.
int maxi = -1;
int ci = (ai+1) % pn;
// Tesselate only outer edges.
if (points[ci*4+3] == 0)
{
int dx = bx - ax;
int dz = bz - az;
if (dx*dx + dz*dz > maxEdgeLen*maxEdgeLen)
{
int n = bi < ai ? (bi+pn - ai) : (bi - ai);
maxi = (ai + n/2) % pn;
}
}
// If the max deviation is larger than accepted error,
// add new point, else continue to next segment.
if (maxi != -1)
{
// Add space for the new point.
simplified.resize(simplified.size()+4);
int n = simplified.size()/4;
for (int j = n-1; j > i; --j)
{
simplified[j*4+0] = simplified[(j-1)*4+0];
simplified[j*4+1] = simplified[(j-1)*4+1];
simplified[j*4+2] = simplified[(j-1)*4+2];
simplified[j*4+3] = simplified[(j-1)*4+3];
}
// Add the point.
simplified[(i+1)*4+0] = points[maxi*4+0];
simplified[(i+1)*4+1] = points[maxi*4+1];
simplified[(i+1)*4+2] = points[maxi*4+2];
simplified[(i+1)*4+3] = maxi;
}
else
{
++i;
}
}
}
for (int i = 0; i < simplified.size()/4; ++i)
{
int ai = (simplified[i*4+3]+1) % pn;
simplified[i*4+3] = points[ai*4+3];
}
}
static void removeDegenerateSegments(rcIntArray& simplified)
{
// Remove adjacent vertices which are equal on xz-plane,
// or else the triangulator will get confused.
for (int i = 0; i < simplified.size()/4; ++i)
{
int ni = i+1;
if (ni >= (simplified.size()/4))
ni = 0;
if (simplified[i*4+0] == simplified[ni*4+0] &&
simplified[i*4+2] == simplified[ni*4+2])
{
// Degenerate segment, remove.
for (int j = i; j < simplified.size()/4-1; ++j)
{
simplified[j*4+0] = simplified[(j+1)*4+0];
simplified[j*4+1] = simplified[(j+1)*4+1];
simplified[j*4+2] = simplified[(j+1)*4+2];
simplified[j*4+3] = simplified[(j+1)*4+3];
}
simplified.pop();
}
}
}
static int calcAreaOfPolygon2D(const int* verts, const int nverts)
{
int area = 0;
for (int i = 0, j = nverts-1; i < nverts; j=i++)
{
const int* vi = &verts[i*4];
const int* vj = &verts[j*4];
area += vi[0] * vj[2] - vj[0] * vi[2];
}
return (area+1) / 2;
}
static void getClosestIndices(const int* vertsa, const int nvertsa,
const int* vertsb, const int nvertsb,
int& ia, int& ib)
{
int closestDist = 0xfffffff;
for (int i = 0; i < nvertsa; ++i)
{
const int* va = &vertsa[i*4];
for (int j = 0; j < nvertsb; ++j)
{
const int* vb = &vertsb[j*4];
const int dx = vb[0] - va[0];
const int dz = vb[2] - va[2];
const int d = dx*dx + dz*dz;
if (d < closestDist)
{
ia = i;
ib = j;
closestDist = d;
}
}
}
}
static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
{
const int maxVerts = ca.nverts + cb.nverts + 2;
int* verts = new int[maxVerts*4];
if (!verts)
return false;
int nv = 0;
// Copy contour A.
for (int i = 0; i <= ca.nverts; ++i)
{
int* dst = &verts[nv*4];
const int* src = &ca.verts[((ia+i)%ca.nverts)*4];
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
nv++;
}
// Copy contour B
for (int i = 0; i <= cb.nverts; ++i)
{
int* dst = &verts[nv*4];
const int* src = &cb.verts[((ib+i)%cb.nverts)*4];
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
nv++;
}
delete [] ca.verts;
ca.verts = verts;
ca.nverts = nv;
delete [] cb.verts;
cb.verts = 0;
cb.nverts = 0;
return true;
}
bool rcBuildContours(rcCompactHeightfield& chf,
float maxError, int maxEdgeLen,
rcContourSet& cset)
{
const int w = chf.width;
const int h = chf.height;
rcTimeVal startTime = rcGetPerformanceTimer();
const int maxContours = chf.maxRegions*2;
cset.conts = new rcContour[maxContours];
if (!cset.conts)
return false;
cset.nconts = 0;
unsigned char* flags = new unsigned char[chf.spanCount];
if (!flags)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'flags'.");
return false;
}
rcTimeVal boundaryStartTime = rcGetPerformanceTimer();
// Mark boundaries.
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
const rcCompactCell& c = chf.cells[x+y*w];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
unsigned char res = 0;
const rcCompactSpan& s = chf.spans[i];
if (s.reg == 0)
{
flags[i] = 0;
continue;
}
for (int dir = 0; dir < 4; ++dir)
{
unsigned short r = 0;
if (rcGetCon(s, dir) != 0xf)
{
const int ax = x + rcGetDirOffsetX(dir);
const int ay = y + rcGetDirOffsetY(dir);
const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
const rcCompactSpan& as = chf.spans[ai];
r = as.reg;
}
if (r == s.reg)
res |= (1 << dir);
}
flags[i] = res ^ 0xf; // Inverse, mark non connected edges.
}
}
}
rcTimeVal boundaryEndTime = rcGetPerformanceTimer();
rcTimeVal contourStartTime = rcGetPerformanceTimer();
rcIntArray verts(256);
rcIntArray simplified(64);
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
const rcCompactCell& c = chf.cells[x+y*w];
for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
{
if (flags[i] == 0 || flags[i] == 0xf)
{
flags[i] = 0;
continue;
}
verts.resize(0);
simplified.resize(0);
walkContour(x, y, i, chf, flags, verts);
simplifyContour(verts, simplified, maxError, maxEdgeLen);
removeDegenerateSegments(simplified);
// Store region->contour remap info.
unsigned short reg = chf.spans[i].reg;
// Create contour.
if (simplified.size()/4 >= 3)
{
if (cset.nconts >= maxContours)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildContours: Too many contours %d, max %d.", cset.nconts, maxContours);
return false;
}
rcContour* cont = &cset.conts[cset.nconts++];
cont->nverts = simplified.size()/4;
cont->verts = new int[cont->nverts*4];
memcpy(cont->verts, &simplified[0], sizeof(int)*cont->nverts*4);
cont->nrverts = verts.size()/4;
cont->rverts = new int[cont->nrverts*4];
memcpy(cont->rverts, &verts[0], sizeof(int)*cont->nrverts*4);
cont->cx = cont->cy = cont->cz = 0;
for (int i = 0; i < cont->nverts; ++i)
{
cont->cx += cont->verts[i*4+0];
cont->cy += cont->verts[i*4+1];
cont->cz += cont->verts[i*4+2];
}
cont->cx /= cont->nverts;
cont->cy /= cont->nverts;
cont->cz /= cont->nverts;
cont->reg = reg;
}
}
}
}
// Check and merge droplings.
// Sometimes the previous algorithms can fail and create several countours
// per area. This pass will try to merge the holes into the main region.
for (int i = 0; i < cset.nconts; ++i)
{
rcContour& cont = cset.conts[i];
// Check if the contour is would backwards.
if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
{
// Find another contour which has the same region ID.
int mergeIdx = -1;
for (int j = 0; j < cset.nconts; ++j)
{
if (i == j) continue;
if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
{
// Make sure the polygon is correctly oriented.
if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
{
mergeIdx = j;
break;
}
}
}
if (mergeIdx == -1)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
}
else
{
rcContour& mcont = cset.conts[mergeIdx];
// Merge by closest points.
int ia, ib;
getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
if (!mergeContours(mcont, cont, ia, ib))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
}
}
}
}
rcTimeVal contourEndTime = rcGetPerformanceTimer();
// Delete vertices.
delete [] flags;
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetLog())
{
rcGetLog()->log(RC_LOG_PROGRESS, "Create contours: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
rcGetLog()->log(RC_LOG_PROGRESS, " - boundary: %.3f ms", rcGetDeltaTimeUsec(boundaryStartTime, boundaryEndTime)/1000.0f);
rcGetLog()->log(RC_LOG_PROGRESS, " - contour: %.3f ms", rcGetDeltaTimeUsec(contourStartTime, contourEndTime)/1000.0f);
}
return true;
}

View File

@ -0,0 +1,491 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include "RecastDebugDraw.h"
#include "SDL.h"
#include "SDL_Opengl.h"
#include "MeshLoaderObj.h"
#include "Recast.h"
void rcDebugDrawMesh(const rcMeshLoaderObj& mesh, const unsigned char* flags)
{
int nt = mesh.getTriCount();
const float* verts = mesh.getVerts();
const float* normals = mesh.getNormals();
const int* tris = mesh.getTris();
glBegin(GL_TRIANGLES);
for (int i = 0; i < nt*3; i += 3)
{
float a = (2+normals[i+0]+normals[i+1])/4;
if (flags && !flags[i/3])
glColor3f(a,a*0.3f,a*0.1f);
else
glColor3f(a,a,a);
glVertex3fv(&verts[tris[i]*3]);
glVertex3fv(&verts[tris[i+1]*3]);
glVertex3fv(&verts[tris[i+2]*3]);
}
glEnd();
}
void drawBoxWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col)
{
glColor4fv(col);
// Top
glVertex3f(minx, miny, minz);
glVertex3f(maxx, miny, minz);
glVertex3f(maxx, miny, minz);
glVertex3f(maxx, miny, maxz);
glVertex3f(maxx, miny, maxz);
glVertex3f(minx, miny, maxz);
glVertex3f(minx, miny, maxz);
glVertex3f(minx, miny, minz);
// bottom
glVertex3f(minx, maxy, minz);
glVertex3f(maxx, maxy, minz);
glVertex3f(maxx, maxy, minz);
glVertex3f(maxx, maxy, maxz);
glVertex3f(maxx, maxy, maxz);
glVertex3f(minx, maxy, maxz);
glVertex3f(minx, maxy, maxz);
glVertex3f(minx, maxy, minz);
// Sides
glVertex3f(minx, miny, minz);
glVertex3f(minx, maxy, minz);
glVertex3f(maxx, miny, minz);
glVertex3f(maxx, maxy, minz);
glVertex3f(maxx, miny, maxz);
glVertex3f(maxx, maxy, maxz);
glVertex3f(minx, miny, maxz);
glVertex3f(minx, maxy, maxz);
}
void drawBox(float minx, float miny, float minz, float maxx, float maxy, float maxz,
const float* col1, const float* col2)
{
float verts[8*3] =
{
minx, miny, minz,
maxx, miny, minz,
maxx, miny, maxz,
minx, miny, maxz,
minx, maxy, minz,
maxx, maxy, minz,
maxx, maxy, maxz,
minx, maxy, maxz,
};
static const float dim[6] =
{
0.95f, 0.55f, 0.65f, 0.85f, 0.65f, 0.85f,
};
static const unsigned char inds[6*5] =
{
0, 7, 6, 5, 4,
1, 0, 1, 2, 3,
2, 1, 5, 6, 2,
3, 3, 7, 4, 0,
4, 2, 6, 7, 3,
5, 0, 4, 5, 1,
};
const unsigned char* in = inds;
for (int i = 0; i < 6; ++i)
{
float d = dim[*in]; in++;
if (i == 0)
glColor4f(d*col2[0],d*col2[1],d*col2[2], col2[3]);
else
glColor4f(d*col1[0],d*col1[1],d*col1[2], col1[3]);
glVertex3fv(&verts[*in*3]); in++;
glVertex3fv(&verts[*in*3]); in++;
glVertex3fv(&verts[*in*3]); in++;
glVertex3fv(&verts[*in*3]); in++;
}
}
void rcDebugDrawCylinderWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col)
{
static const int NUM_SEG = 16;
float dir[NUM_SEG*2];
for (int i = 0; i < NUM_SEG; ++i)
{
const float a = (float)i/(float)NUM_SEG*(float)M_PI*2;
dir[i*2] = cosf(a);
dir[i*2+1] = sinf(a);
}
const float cx = (maxx + minx)/2;
const float cz = (maxz + minz)/2;
const float rx = (maxx - minx)/2;
const float rz = (maxz - minz)/2;
glColor4fv(col);
glBegin(GL_LINES);
for (int i = 0, j=NUM_SEG-1; i < NUM_SEG; j=i++)
{
glVertex3f(cx+dir[j*2+0]*rx, miny, cz+dir[j*2+1]*rz);
glVertex3f(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz);
glVertex3f(cx+dir[j*2+0]*rx, maxy, cz+dir[j*2+1]*rz);
glVertex3f(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz);
}
for (int i = 0; i < NUM_SEG; i += NUM_SEG/4)
{
glVertex3f(cx+dir[i*2+0]*rx, miny, cz+dir[i*2+1]*rz);
glVertex3f(cx+dir[i*2+0]*rx, maxy, cz+dir[i*2+1]*rz);
}
glEnd();
}
void rcDebugDrawBoxWire(float minx, float miny, float minz, float maxx, float maxy, float maxz, const float* col)
{
glBegin(GL_LINES);
drawBoxWire(minx, miny, minz, maxx, maxy, maxz, col);
glEnd();
}
void rcDebugDrawBox(float minx, float miny, float minz, float maxx, float maxy, float maxz,
const float* col1, const float* col2)
{
glBegin(GL_QUADS);
drawBox(minx, miny, minz, maxx, maxy, maxz, col1, col2);
glEnd();
}
void rcDebugDrawHeightfieldSolid(const rcHeightfield& hf,
const float* orig, float cs, float ch)
{
static const float col0[4] = { 1,1,1,1 };
const int w = hf.width;
const int h = hf.height;
glBegin(GL_QUADS);
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
float fx = orig[0] + x*cs;
float fz = orig[2] + y*cs;
const rcSpan* s = hf.spans[x + y*w];
while (s)
{
drawBox(fx, orig[1]+s->smin*ch, fz, fx+cs, orig[1] + s->smax*ch, fz+cs, col0, col0);
s = s->next;
}
}
}
glEnd();
}
void rcDebugDrawHeightfieldWalkable(const rcHeightfield& hf,
const float* orig, float cs, float ch)
{
static const float col0[4] = { 1,1,1,1 };
static const float col1[4] = { 0.25f,0.44f,0.5f,1 };
const int w = hf.width;
const int h = hf.height;
glBegin(GL_QUADS);
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
float fx = orig[0] + x*cs;
float fz = orig[2] + y*cs;
const rcSpan* s = hf.spans[x + y*w];
while (s)
{
bool csel = (s->flags & 0x2) == 0;
drawBox(fx, orig[1]+s->smin*ch, fz, fx+cs, orig[1] + s->smax*ch, fz+cs, col0, csel ? col0 : col1);
s = s->next;
}
}
}
glEnd();
}
void rcDebugDrawCompactHeightfieldSolid(const rcCompactHeightfield& chf)
{
const float cs = chf.cs;
const float ch = chf.ch;
glColor3ub(64,112,128);
glBegin(GL_QUADS);
for (int y = 0; y < chf.height; ++y)
{
for (int x = 0; x < chf.width; ++x)
{
const float fx = chf.minx + x*cs;
const float fz = chf.minz + y*cs;
const rcCompactCell& c = chf.cells[x+y*chf.width];
for (unsigned i = c.index, ni = c.index+c.count; i < ni; ++i)
{
const rcCompactSpan& s = chf.spans[i];
const float fy = chf.miny + (s.y+1)*ch;
glVertex3f(fx, fy, fz);
glVertex3f(fx, fy, fz+cs);
glVertex3f(fx+cs, fy, fz+cs);
glVertex3f(fx+cs, fy, fz);
}
}
}
glEnd();
}
void rcDebugDrawCompactHeightfieldRegions(const rcCompactHeightfield& chf)
{
const float cs = chf.cs;
const float ch = chf.ch;
float col[4] = { 1,1,1,1 };
glBegin(GL_QUADS);
for (int y = 0; y < chf.height; ++y)
{
for (int x = 0; x < chf.width; ++x)
{
const float fx = chf.minx + x*cs;
const float fz = chf.minz + y*cs;
const rcCompactCell& c = chf.cells[x+y*chf.width];
for (unsigned i = c.index, ni = c.index+c.count; i < ni; ++i)
{
const rcCompactSpan& s = chf.spans[i];
if (s.reg)
{
intToCol(s.reg, col);
glColor4fv(col);
}
else
glColor4ub(0,0,0,128);
const float fy = chf.miny + (s.y+1)*ch;
glVertex3f(fx, fy, fz);
glVertex3f(fx, fy, fz+cs);
glVertex3f(fx+cs, fy, fz+cs);
glVertex3f(fx+cs, fy, fz);
}
}
}
glEnd();
}
void rcDebugDrawCompactHeightfieldDistance(const rcCompactHeightfield& chf)
{
const float cs = chf.cs;
const float ch = chf.ch;
float maxd = chf.maxDistance;
if (maxd < 1.0f) maxd = 1;
float dscale = 1.0f / maxd;
glBegin(GL_QUADS);
for (int y = 0; y < chf.height; ++y)
{
for (int x = 0; x < chf.width; ++x)
{
const float fx = chf.minx + x*cs;
const float fz = chf.minz + y*cs;
const rcCompactCell& c = chf.cells[x+y*chf.width];
for (unsigned i = c.index, ni = c.index+c.count; i < ni; ++i)
{
const rcCompactSpan& s = chf.spans[i];
const float fy = chf.miny + (s.y+1)*ch;
float cd = (float)s.dist * dscale;
glColor3f(cd, cd, cd);
glVertex3f(fx, fy, fz);
glVertex3f(fx, fy, fz+cs);
glVertex3f(fx+cs, fy, fz+cs);
glVertex3f(fx+cs, fy, fz);
}
}
}
glEnd();
}
void rcDebugDrawRawContours(const rcContourSet& cset, const float* orig, float cs, float ch)
{
float col[4] = { 1,1,1,1 };
glLineWidth(3.0f);
for (int i = 0; i < cset.nconts; ++i)
{
const rcContour& c = cset.conts[i];
intToCol(c.reg, col);
glColor4fv(col);
glBegin(GL_LINE_LOOP);
for (int j = 0; j < c.nrverts; ++j)
{
const int* v = &c.rverts[j*4];
float fx = orig[0] + v[0]*cs;
float fy = orig[1] + (v[1]+1+(i&1))*ch;
float fz = orig[2] + v[2]*cs;
glVertex3f(fx,fy,fz);
}
glEnd();
}
glLineWidth(1.0f);
}
void rcDebugDrawContours(const rcContourSet& cset, const float* orig, float cs, float ch)
{
float col[4] = { 1,1,1,1 };
glLineWidth(3.0f);
glPointSize(4.0f);
for (int i = 0; i < cset.nconts; ++i)
{
const rcContour& c = cset.conts[i];
intToCol(c.reg, col);
glColor4fv(col);
glBegin(GL_LINE_LOOP);
for (int j = 0; j < c.nverts; ++j)
{
const int* v = &c.verts[j*4];
float fx = orig[0] + v[0]*cs;
float fy = orig[1] + (v[1]+1+(i&1))*ch;
float fz = orig[2] + v[2]*cs;
glVertex3f(fx,fy,fz);
}
glEnd();
glColor4ub(0,0,0,128);
glBegin(GL_POINTS);
for (int j = 0; j < c.nverts; ++j)
{
const int* v = &c.verts[j*4];
float fx = orig[0] + v[0]*cs;
float fy = orig[1] + (v[1]+1+(i&1))*ch;
float fz = orig[2] + v[2]*cs;
glVertex3f(fx,fy,fz);
}
glEnd();
}
glLineWidth(1.0f);
glPointSize(1.0f);
}
void rcDebugDrawPolyMesh(const struct rcPolyMesh& mesh, const float* orig, float cs, float ch)
{
const int nvp = mesh.nvp;
glColor4ub(0,196,255,64);
glBegin(GL_TRIANGLES);
for (int i = 0; i < mesh.npolys; ++i)
{
const unsigned short* p = &mesh.polys[i*nvp*2];
unsigned short vi[3];
for (int j = 2; j < nvp; ++j)
{
if (p[j] == 0xffff) break;
vi[0] = p[0];
vi[1] = p[j-1];
vi[2] = p[j];
for (int k = 0; k < 3; ++k)
{
const unsigned short* v = &mesh.verts[vi[k]*3];
const float x = orig[0] + v[0]*cs;
const float y = orig[1] + (v[1]+2)*ch;
const float z = orig[2] + v[2]*cs;
glVertex3f(x, y, z);
}
}
}
glEnd();
// Draw tri boundaries
glColor4ub(0,0,0,64);
glLineWidth(1.0f);
glBegin(GL_LINES);
for (int i = 0; i < mesh.npolys; ++i)
{
const unsigned short* poly = &mesh.polys[i*nvp*2];
for (int j = 0; j < nvp; ++j)
{
if (poly[j] == 0xffff) break;
if (poly[nvp+j] == 0xffff) continue;
int vi[2];
vi[0] = poly[j];
if (j+1 >= nvp || poly[j+1] == 0xffff)
vi[1] = poly[0];
else
vi[1] = poly[j+1];
for (int k = 0; k < 2; ++k)
{
const unsigned short* v = &mesh.verts[vi[k]*3];
const float x = orig[0] + v[0]*cs;
const float y = orig[1] + (v[1]+2)*ch + 0.1f;
const float z = orig[2] + v[2]*cs;
glVertex3f(x, y, z);
}
}
}
glEnd();
// Draw boundaries
glLineWidth(3.0f);
glColor4ub(0,0,0,128);
glBegin(GL_LINES);
for (int i = 0; i < mesh.npolys; ++i)
{
const unsigned short* poly = &mesh.polys[i*nvp*2];
for (int j = 0; j < nvp; ++j)
{
if (poly[j] == 0xffff) break;
if (poly[nvp+j] != 0xffff) continue;
int vi[2];
vi[0] = poly[j];
if (j+1 >= nvp || poly[j+1] == 0xffff)
vi[1] = poly[0];
else
vi[1] = poly[j+1];
for (int k = 0; k < 2; ++k)
{
const unsigned short* v = &mesh.verts[vi[k]*3];
const float x = orig[0] + v[0]*cs;
const float y = orig[1] + (v[1]+2)*ch + 0.1f;
const float z = orig[2] + v[2]*cs;
glVertex3f(x, y, z);
}
}
}
glEnd();
glLineWidth(1.0f);
glPointSize(4.0f);
glColor4ub(0,0,0,128);
glBegin(GL_POINTS);
for (int i = 0; i < mesh.nverts; ++i)
{
const unsigned short* v = &mesh.verts[i*3];
const float x = orig[0] + v[0]*cs;
const float y = orig[1] + (v[1]+2)*ch + 0.1f;
const float z = orig[2] + v[2]*cs;
glVertex3f(x, y, z);
}
glEnd();
glPointSize(1.0f);
}

View File

@ -0,0 +1,232 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include "Recast.h"
#include "RecastLog.h"
#include "RecastTimer.h"
void rcFilterWalkableBorderSpans(const int walkableHeight,
const int walkableClimb,
rcHeightfield& solid)
{
rcTimeVal startTime = rcGetPerformanceTimer();
const int w = solid.width;
const int h = solid.height;
const int MAX_HEIGHT = 0xffff;
// Mark border spans.
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
{
// Skip non walkable spans.
if ((s->flags & RC_WALKABLE) == 0)
continue;
// The span is valid only if it has four neighbours.
int neighbourCount = 0;
const int bot = (int)s->smax;
const int top = (int)s->next ? (int)s->next->smin : MAX_HEIGHT;
// Visit neighbours in all 4 directions.
for (int dir = 0; dir < 4; ++dir)
{
int dx = x + rcGetDirOffsetX(dir);
int dy = y + rcGetDirOffsetY(dir);
// Skip neighbours which are out of bounds.
if (dx < 0 || dy < 0 || dx >= w || dy >= h)
continue;
for (rcSpan* ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
{
const int nbot = (int)ns->smax;
const int ntop = (int)ns->next ? (int)ns->next->smin : MAX_HEIGHT;
// Skip neightbour if the gap between the spans is too small.
if (rcMin(top,ntop) - rcMax(bot,nbot) <= walkableHeight)
continue;
// Skip neightbour if the climb height to the neighbour is too high.
if (rcAbs(nbot - bot) >= walkableClimb)
continue;
// This neighbour is reachable.
neighbourCount++;
}
}
// Remove walkable flag.
if (neighbourCount != 4)
s->flags &= ~RC_WALKABLE;
}
}
}
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetLog())
rcGetLog()->log(RC_LOG_PROGRESS, "Filter border: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
}
void rcFilterWalkableLowHeightSpans(int walkableHeight,
rcHeightfield& solid)
{
rcTimeVal startTime = rcGetPerformanceTimer();
const int w = solid.width;
const int h = solid.height;
const int MAX_HEIGHT = 0xffff;
// Remove walkable flag from spans which do not have enough
// space above them for the agent to stand there.
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
for (rcSpan* s = solid.spans[x + y*w]; s; s = s->next)
{
const int bot = (int)s->smax;
const int top = (int)s->next ? (int)s->next->smin : MAX_HEIGHT;
if ((top - bot) <= walkableHeight)
s->flags &= ~RC_WALKABLE;
}
}
}
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetLog())
rcGetLog()->log(RC_LOG_PROGRESS, "Filter walkable: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
}
struct rcReachableSeed
{
inline void set(int ix, int iy, rcSpan* is)
{
x = (unsigned short)ix;
y = (unsigned short)iy;
s = is;
}
unsigned short x, y;
rcSpan* s;
};
bool rcMarkReachableSpans(const int walkableHeight,
const int walkableClimb,
rcHeightfield& solid)
{
const int w = solid.width;
const int h = solid.height;
const int MAX_HEIGHT = 0xffff;
rcTimeVal startTime = rcGetPerformanceTimer();
// Build navigable space.
const int MAX_SEEDS = w*h;
rcReachableSeed* stack = new rcReachableSeed[MAX_SEEDS];
if (!stack)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcMarkReachableSpans: Out of memory 'stack' (%d).", MAX_SEEDS);
return false;
}
int stackSize = 0;
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
rcSpan* topSpan = solid.spans[x + y*w];
if (!topSpan)
continue;
while (topSpan->next)
topSpan = topSpan->next;
// If the span is not walkable, skip it.
if ((topSpan->flags & RC_WALKABLE) == 0)
continue;
// If the span has been visited already, skip it.
if (topSpan->flags & RC_REACHABLE)
continue;
// Start flood fill.
topSpan->flags |= RC_REACHABLE;
stackSize = 0;
stack[stackSize].set(x, y, topSpan);
stackSize++;
while (stackSize)
{
// Pop a seed from the stack.
stackSize--;
rcReachableSeed cur = stack[stackSize];
const int bot = (int)cur.s->smax;
const int top = (int)cur.s->next ? (int)cur.s->next->smin : MAX_HEIGHT;
// Visit neighbours in all 4 directions.
for (int dir = 0; dir < 4; ++dir)
{
int dx = (int)cur.x + rcGetDirOffsetX(dir);
int dy = (int)cur.y + rcGetDirOffsetY(dir);
// Skip neighbour which are out of bounds.
if (dx < 0 || dy < 0 || dx >= w || dy >= h)
continue;
for (rcSpan* ns = solid.spans[dx + dy*w]; ns; ns = ns->next)
{
// Skip neighbour if it is not walkable.
if ((ns->flags & RC_WALKABLE) == 0)
continue;
// Skip the neighbour if it has been visited already.
if (ns->flags & RC_REACHABLE)
continue;
const int nbot = (int)ns->smax;
const int ntop = (int)ns->next ? (int)ns->next->smin : MAX_HEIGHT;
// Skip neightbour if the gap between the spans is too small.
if (rcMin(top,ntop) - rcMax(bot,nbot) < walkableHeight)
continue;
// Skip neightbour if the climb height to the neighbour is too high.
if (rcAbs(nbot - bot) >= walkableClimb)
continue;
// This neighbour has not been visited yet.
// Mark it as reachable and add it to the seed stack.
ns->flags |= RC_REACHABLE;
if (stackSize < MAX_SEEDS)
{
stack[stackSize].set(dx, dy, ns);
stackSize++;
}
}
}
}
}
}
delete [] stack;
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetLog())
rcGetLog()->log(RC_LOG_PROGRESS, "Mark reachable: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
return true;
}

View File

@ -0,0 +1,66 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#include "RecastLog.h"
#include <stdio.h>
#include <stdarg.h>
static rcLog* g_log = 0;
rcLog::rcLog() :
m_messageCount(0),
m_textPoolSize(0)
{
}
rcLog::~rcLog()
{
if (g_log == this)
g_log = 0;
}
void rcLog::log(rcLogCategory category, const char* format, ...)
{
if (m_messageCount >= MAX_MESSAGES)
return;
char* dst = &m_textPool[m_textPoolSize];
int n = TEXT_POOL_SIZE - m_textPoolSize;
if (n < 2)
return;
// Store category
*dst = (char)category;
n--;
// Store message
va_list ap;
va_start(ap, format);
int ret = vsnprintf(dst+1, n-1, format, ap);
va_end(ap);
if (ret > 0)
m_textPoolSize += ret+2;
m_messages[m_messageCount++] = dst;
}
void rcSetLog(rcLog* log)
{
g_log = log;
}
rcLog* rcGetLog()
{
return g_log;
}

View File

@ -0,0 +1,699 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "Recast.h"
#include "RecastLog.h"
#include "RecastTimer.h"
struct rcEdge
{
unsigned short vert[2];
unsigned short polyEdge[2];
unsigned short poly[2];
};
static bool buildMeshAdjacency(unsigned short* polys, const int npolys,
const int nverts, const int vertsPerPoly)
{
// Based on code by Eric Lengyel from:
// http://www.terathon.com/code/edges.php
int maxEdgeCount = npolys*vertsPerPoly;
unsigned short* firstEdge = new unsigned short[nverts + maxEdgeCount];
if (!firstEdge)
return false;
unsigned short* nextEdge = firstEdge + nverts;
int edgeCount = 0;
rcEdge* edges = new rcEdge[maxEdgeCount];
if (!edges)
return false;
for (int i = 0; i < nverts; i++)
firstEdge[i] = 0xffff;
// Invalida indices are marked as 0xffff, the following code
// handles them just fine.
for (int i = 0; i < npolys; ++i)
{
unsigned short* t = &polys[i*vertsPerPoly*2];
for (int j = 0; j < vertsPerPoly; ++j)
{
unsigned short v0 = t[j];
unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == 0xffff) ? t[0] : t[j+1];
if (v0 < v1)
{
rcEdge& edge = edges[edgeCount];
edge.vert[0] = v0;
edge.vert[1] = v1;
edge.poly[0] = (unsigned short)i;
edge.polyEdge[0] = (unsigned short)j;
edge.poly[1] = (unsigned short)i;
edge.polyEdge[1] = 0;
// Insert edge
nextEdge[edgeCount] = firstEdge[v0];
firstEdge[v0] = edgeCount;
edgeCount++;
}
}
}
for (int i = 0; i < npolys; ++i)
{
unsigned short* t = &polys[i*vertsPerPoly*2];
for (int j = 0; j < vertsPerPoly; ++j)
{
unsigned short v0 = t[j];
unsigned short v1 = (j+1 >= vertsPerPoly || t[j+1] == 0xffff) ? t[0] : t[j+1];
if (v0 > v1)
{
for (unsigned short e = firstEdge[v1]; e != 0xffff; e = nextEdge[e])
{
rcEdge& edge = edges[e];
if (edge.vert[1] == v0 && edge.poly[0] == edge.poly[1])
{
edge.poly[1] = (unsigned short)i;
edge.polyEdge[1] = (unsigned short)j;
break;
}
}
}
}
}
// Store adjacency
for (int i = 0; i < edgeCount; ++i)
{
const rcEdge& e = edges[i];
if (e.poly[0] != e.poly[1])
{
unsigned short* p0 = &polys[e.poly[0]*vertsPerPoly*2];
unsigned short* p1 = &polys[e.poly[1]*vertsPerPoly*2];
p0[vertsPerPoly + e.polyEdge[0]] = e.poly[1];
p1[vertsPerPoly + e.polyEdge[1]] = e.poly[0];
}
}
delete [] firstEdge;
delete [] edges;
return true;
}
static const int VERTEX_BUCKET_COUNT = (1<<12);
inline int computeVertexHash(int x, int y, int z)
{
const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
const unsigned int h3 = 0xcb1ab31f;
unsigned int n = h1 * x + h2 * y + h3 * z;
return (int)(n & (VERTEX_BUCKET_COUNT-1));
}
static int addVertex(unsigned short x, unsigned short y, unsigned short z,
unsigned short* verts, int* firstVert, int* nextVert, int& nv)
{
int bucket = computeVertexHash(x, y, z);
int i = firstVert[bucket];
while (i != -1)
{
const unsigned short* v = &verts[i*3];
if (v[0] == x && v[1] == y && v[2] == z)
return i;
i = nextVert[i]; // next
}
// Could not find, create new.
i = nv; nv++;
unsigned short* v = &verts[i*3];
v[0] = x;
v[1] = y;
v[2] = z;
nextVert[i] = firstVert[bucket];
firstVert[bucket] = i;
return i;
}
inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
inline int area2(const int* a, const int* b, const int* c)
{
return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]);
}
// Exclusive or: true iff exactly one argument is true.
// The arguments are negated to ensure that they are 0/1
// values. Then the bitwise Xor operator may apply.
// (This idea is due to Michael Baldwin.)
inline bool xorb(bool x, bool y)
{
return !x ^ !y;
}
// Returns true iff c is strictly to the left of the directed
// line through a to b.
inline bool left(const int* a, const int* b, const int* c)
{
return area2(a, b, c) < 0;
}
inline bool leftOn(const int* a, const int* b, const int* c)
{
return area2(a, b, c) <= 0;
}
inline bool collinear(const int* a, const int* b, const int* c)
{
return area2(a, b, c) == 0;
}
// Returns true iff ab properly intersects cd: they share
// a point interior to both segments. The properness of the
// intersection is ensured by using strict leftness.
bool intersectProp(const int* a, const int* b, const int* c, const int* d)
{
// Eliminate improper cases.
if (collinear(a,b,c) || collinear(a,b,d) ||
collinear(c,d,a) || collinear(c,d,b))
return false;
return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
}
// Returns T iff (a,b,c) are collinear and point c lies
// on the closed segement ab.
static bool between(const int* a, const int* b, const int* c)
{
if (!collinear(a, b, c))
return false;
// If ab not vertical, check betweenness on x; else on y.
if (a[0] != b[0])
return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
else
return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
}
// Returns true iff segments ab and cd intersect, properly or improperly.
static bool intersect(const int* a, const int* b, const int* c, const int* d)
{
if (intersectProp(a, b, c, d))
return true;
else if (between(a, b, c) || between(a, b, d) ||
between(c, d, a) || between(c, d, b))
return true;
else
return false;
}
static bool vequal(const int* a, const int* b)
{
return a[0] == b[0] && a[2] == b[2];
}
// Returns T iff (v_i, v_j) is a proper internal *or* external
// diagonal of P, *ignoring edges incident to v_i and v_j*.
static bool diagonalie(int i, int j, int n, const int* verts, int* indices)
{
const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
// For each edge (k,k+1) of P
for (int k = 0; k < n; k++)
{
int k1 = next(k, n);
// Skip edges incident to i or j
if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
{
const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
continue;
if (intersect(d0, d1, p0, p1))
return false;
}
}
return true;
}
// Returns true iff the diagonal (i,j) is strictly internal to the
// polygon P in the neighborhood of the i endpoint.
static bool inCone(int i, int j, int n, const int* verts, int* indices)
{
const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
// If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
if (leftOn(pin1, pi, pi1))
return left(pi, pj, pin1) && left(pj, pi, pi1);
// Assume (i-1,i,i+1) not collinear.
// else P[i] is reflex.
return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
}
// Returns T iff (v_i, v_j) is a proper internal
// diagonal of P.
static bool diagonal(int i, int j, int n, const int* verts, int* indices)
{
return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
}
int triangulate(int n, const int* verts, int* indices, int* tris)
{
int ntris = 0;
int* dst = tris;
// The last bit of the index is used to indicate if the vertex can be removed.
for (int i = 0; i < n; i++)
{
int i1 = next(i, n);
int i2 = next(i1, n);
if (diagonal(i, i2, n, verts, indices))
indices[i1] |= 0x80000000;
}
while (n > 3)
{
int minLen = -1;
int mini = -1;
for (int i = 0; i < n; i++)
{
int i1 = next(i, n);
if (indices[i1] & 0x80000000)
{
const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
const int* p2 = &verts[(indices[next(i1, n)] & 0x0fffffff) * 4];
int dx = p2[0] - p0[0];
int dy = p2[2] - p0[2];
int len = dx*dx + dy*dy;
if (minLen < 0 || len < minLen)
{
minLen = len;
mini = i;
}
}
}
if (mini == -1)
{
// Should not happen.
if (rcGetLog())
rcGetLog()->log(RC_LOG_WARNING, "triangulate: Failed to triangulate polygon.");
/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
for (int i = 0; i < n; i++)
{
printf("%d ", indices[i] & 0x0fffffff);
}
printf("\n");*/
return -ntris;
}
int i = mini;
int i1 = next(i, n);
int i2 = next(i1, n);
*dst++ = indices[i] & 0x0fffffff;
*dst++ = indices[i1] & 0x0fffffff;
*dst++ = indices[i2] & 0x0fffffff;
ntris++;
// Removes P[i1] by copying P[i+1]...P[n-1] left one index.
n--;
for (int k = i1; k < n; k++)
indices[k] = indices[k+1];
if (i1 >= n) i1 = 0;
i = prev(i1,n);
// Update diagonal flags.
if (diagonal(prev(i, n), i1, n, verts, indices))
indices[i] |= 0x80000000;
else
indices[i] &= 0x0fffffff;
if (diagonal(i, next(i1, n), n, verts, indices))
indices[i1] |= 0x80000000;
else
indices[i1] &= 0x0fffffff;
}
// Append the remaining triangle.
*dst++ = indices[0] & 0x0fffffff;
*dst++ = indices[1] & 0x0fffffff;
*dst++ = indices[2] & 0x0fffffff;
ntris++;
return ntris;
}
static int countPolyVerts(const unsigned short* p, const int nvp)
{
for (int i = 0; i < nvp; ++i)
if (p[i] == 0xffff)
return i;
return nvp;
}
inline bool uleftOn(const unsigned short* a, const unsigned short* b, const unsigned short* c)
{
return ((int)b[0] - (int)a[0]) * ((int)c[2] - (int)a[2]) -
((int)c[0] - (int)a[0]) * ((int)b[2] - (int)a[2]) <= 0;
}
static int getPolyMergeValue(unsigned short* pa, unsigned short* pb,
const unsigned short* verts, int& ea, int& eb,
const int nvp)
{
const int na = countPolyVerts(pa, nvp);
const int nb = countPolyVerts(pb, nvp);
// If the merged polygon would be too big, do not merge.
if (na+nb-2 > nvp)
return -1;
// Check if the polygons share an edge.
ea = -1;
eb = -1;
for (int i = 0; i < na; ++i)
{
unsigned short va0 = pa[i];
unsigned short va1 = pa[(i+1) % na];
if (va0 > va1)
rcSwap(va0, va1);
for (int j = 0; j < nb; ++j)
{
unsigned short vb0 = pb[j];
unsigned short vb1 = pb[(j+1) % nb];
if (vb0 > vb1)
rcSwap(vb0, vb1);
if (va0 == vb0 && va1 == vb1)
{
ea = i;
eb = j;
break;
}
}
}
// No common edge, cannot merge.
if (ea == -1 || eb == -1)
return -1;
// Check to see if the merged polygon would be convex.
unsigned short va, vb, vc;
va = pa[(ea+na-1) % na];
vb = pa[ea];
vc = pb[(eb+2) % nb];
if (!uleftOn(&verts[va*3], &verts[vb*3], &verts[vc*3]))
return -1;
va = pb[(eb+nb-1) % nb];
vb = pb[eb];
vc = pa[(ea+2) % na];
if (!uleftOn(&verts[va*3], &verts[vb*3], &verts[vc*3]))
return -1;
va = pa[ea];
vb = pa[(ea+1)%na];
int dx = (int)verts[va*3+0] - (int)verts[vb*3+0];
int dy = (int)verts[va*3+2] - (int)verts[vb*3+2];
return dx*dx + dy*dy;
}
static void mergePolys(unsigned short* pa, unsigned short* pb,
const unsigned short* verts, int ea, int eb,
unsigned short* tmp, const int nvp)
{
const int na = countPolyVerts(pa, nvp);
const int nb = countPolyVerts(pb, nvp);
// Merge polygons.
memset(tmp, 0xff, sizeof(unsigned short)*nvp);
int n = 0;
// Add pa
for (int i = 0; i < na-1; ++i)
tmp[n++] = pa[(ea+1+i) % na];
// Add pb
for (int i = 0; i < nb-1; ++i)
tmp[n++] = pb[(eb+1+i) % nb];
memcpy(pa, tmp, sizeof(unsigned short)*nvp);
}
bool rcBuildPolyMesh(rcContourSet& cset, rcPolyMesh& mesh, const int nvp)
{
rcTimeVal startTime = rcGetPerformanceTimer();
int maxVertices = 0;
int maxTris = 0;
int maxVertsPerCont = 0;
for (int i = 0; i < cset.nconts; ++i)
{
maxVertices += cset.conts[i].nverts;
maxTris += cset.conts[i].nverts - 2;
maxVertsPerCont = rcMax(maxVertsPerCont, cset.conts[i].nverts);
}
if (maxVertices >= 0xfffe)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Too many vertices %d.", maxVertices);
return false;
}
mesh.verts = new unsigned short[maxVertices*3];
if (!mesh.verts)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxVertices);
return false;
}
mesh.polys = new unsigned short[maxTris*nvp*2];
if (!mesh.polys)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'mesh.verts' (%d).", maxTris*nvp);
return false;
}
mesh.nverts = 0;
mesh.npolys = 0;
mesh.nvp = nvp;
memset(mesh.verts, 0, sizeof(unsigned short)*maxVertices*3);
memset(mesh.polys, 0xff, sizeof(unsigned short)*maxTris*nvp*2);
int* nextVert = new int[maxVertices];
if (!nextVert)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'nextVert' (%d).", maxVertices);
return false;
}
memset(nextVert, 0, sizeof(int)*maxVertices);
int* firstVert = new int[VERTEX_BUCKET_COUNT];
if (!firstVert)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'firstVert' (%d).", VERTEX_BUCKET_COUNT);
return false;
}
for (int i = 0; i < VERTEX_BUCKET_COUNT; ++i)
firstVert[i] = -1;
int* indices = new int[maxVertsPerCont];
if (!indices)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'indices' (%d).", maxVertsPerCont);
return false;
}
int* tris = new int[maxVertsPerCont*3];
if (!tris)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tris' (%d).", maxVertsPerCont*3);
return false;
}
unsigned short* polys = new unsigned short[maxVertsPerCont*nvp];
if (!polys)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'polys' (%d).", maxVertsPerCont*nvp);
return false;
}
unsigned short* tmpPoly = new unsigned short[nvp];
if (!tmpPoly)
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Out of memory 'tmpPoly' (%d).", nvp);
return false;
}
for (int i = 0; i < cset.nconts; ++i)
{
rcContour& cont = cset.conts[i];
// Skip empty contours.
if (cont.nverts < 3)
continue;
// Triangulate contour
for (int j = 0; j < cont.nverts; ++j)
indices[j] = j;
int ntris = triangulate(cont.nverts, cont.verts, &indices[0], &tris[0]);
if (ntris <= 0)
{
// Bad triangulation, should not happen.
/* for (int k = 0; k < cont.nverts; ++k)
{
const int* v = &cont.verts[k*4];
printf("\t\t%d,%d,%d,%d,\n", v[0], v[1], v[2], v[3]);
if (nBadPos < 100)
{
badPos[nBadPos*3+0] = v[0];
badPos[nBadPos*3+1] = v[1];
badPos[nBadPos*3+2] = v[2];
nBadPos++;
}
}*/
ntris = -ntris;
}
// Add and merge vertices.
for (int j = 0; j < cont.nverts; ++j)
{
const int* v = &cont.verts[j*4];
indices[j] = addVertex((unsigned short)v[0], (unsigned short)v[1], (unsigned short)v[2],
mesh.verts, firstVert, nextVert, mesh.nverts);
}
// Build initial polygons.
int npolys = 0;
memset(polys, 0xff, maxVertsPerCont*nvp*sizeof(unsigned short));
for (int j = 0; j < ntris; ++j)
{
int* t = &tris[j*3];
if (t[0] != t[1] && t[0] != t[2] && t[1] != t[2])
{
polys[npolys*nvp+0] = (unsigned short)indices[t[0]];
polys[npolys*nvp+1] = (unsigned short)indices[t[1]];
polys[npolys*nvp+2] = (unsigned short)indices[t[2]];
npolys++;
}
}
if (!npolys)
continue;
// Merge polygons.
if (nvp > 3)
{
while (true)
{
// Find best polygons to merge.
int bestMergeVal = 0;
int bestPa, bestPb, bestEa, bestEb;
for (int j = 0; j < npolys-1; ++j)
{
unsigned short* pj = &polys[j*nvp];
for (int k = j+1; k < npolys; ++k)
{
unsigned short* pk = &polys[k*nvp];
int ea, eb;
int v = getPolyMergeValue(pj, pk, mesh.verts, ea, eb, nvp);
if (v > bestMergeVal)
{
bestMergeVal = v;
bestPa = j;
bestPb = k;
bestEa = ea;
bestEb = eb;
}
}
}
if (bestMergeVal > 0)
{
// Found best, merge.
unsigned short* pa = &polys[bestPa*nvp];
unsigned short* pb = &polys[bestPb*nvp];
mergePolys(pa, pb, mesh.verts, bestEa, bestEb, tmpPoly, nvp);
memcpy(pb, &polys[(npolys-1)*nvp], sizeof(unsigned short)*nvp);
npolys--;
}
else
{
// Could not merge any polygons, stop.
break;
}
}
}
// Store polygons.
for (int j = 0; j < npolys; ++j)
{
unsigned short* p = &mesh.polys[mesh.npolys*nvp*2];
unsigned short* q = &polys[j*nvp];
for (int k = 0; k < nvp; ++k)
p[k] = q[k];
mesh.npolys++;
}
}
delete [] tmpPoly;
delete [] firstVert;
delete [] nextVert;
delete [] indices;
delete [] tris;
// Calculate adjacency.
if (!buildMeshAdjacency(mesh.polys, mesh.npolys, mesh.nverts, nvp))
{
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "rcBuildPolyMesh: Adjacency failed.");
return false;
}
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetLog())
rcGetLog()->log(RC_LOG_ERROR, "Build polymesh: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
return true;
}

View File

@ -0,0 +1,295 @@
//
// Copyright (c) 2009 Mikko Mononen memon@inside.org
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdio.h>
#include "Recast.h"
#include "RecastTimer.h"
#include "RecastLog.h"
inline bool overlapBounds(const float* amin, const float* amax, const float* bmin, const float* bmax)
{
bool overlap = true;
overlap = (amin[0] > bmax[0] || amax[0] < bmin[0]) ? false : overlap;
overlap = (amin[1] > bmax[1] || amax[1] < bmin[1]) ? false : overlap;
overlap = (amin[2] > bmax[2] || amax[2] < bmin[2]) ? false : overlap;
return overlap;
}
inline bool overlapInterval(unsigned short amin, unsigned short amax,
unsigned short bmin, unsigned short bmax)
{
if (amax < bmin) return false;
if (amin > bmax) return false;
return true;
}
static rcSpan* allocSpan(rcHeightfield& hf)
{
static const int SPANS_PER_POOL = 2048;
// If running out of memory, allocate new page and update the freelist.
if (!hf.freelist || !hf.freelist->next)
{
// Create new page.
// Allocate memory for the new pool.
const int size = (sizeof(rcSpanPool)-sizeof(rcSpan)) + sizeof(rcSpan)*SPANS_PER_POOL;
rcSpanPool* pool = reinterpret_cast<rcSpanPool*>(new unsigned char[size]);
if (!pool) return 0;
pool->next = 0;
// Add the pool into the list of pools.
pool->next = hf.pools;
hf.pools = pool;
// Add new items to the free list.
rcSpan* freelist = hf.freelist;
rcSpan* head = &pool->items[0];
rcSpan* it = &pool->items[SPANS_PER_POOL];
do
{
--it;
it->next = freelist;
freelist = it;
}
while (it != head);
hf.freelist = it;
}
// Pop item from in front of the free list.
rcSpan* it = hf.freelist;
hf.freelist = hf.freelist->next;
return it;
}
static void freeSpan(rcHeightfield& hf, rcSpan* ptr)
{
if (!ptr) return;
// Add the node in front of the free list.
ptr->next = hf.freelist;
hf.freelist = ptr;
}
static void addSpan(rcHeightfield& hf, int x, int y,
unsigned short smin, unsigned short smax,
unsigned short flags)
{
int idx = x + y*hf.width;
rcSpan* s = allocSpan(hf);
s->smin = smin;
s->smax = smax;
s->flags = flags;
s->next = 0;
// Empty cell, add he first span.
if (!hf.spans[idx])
{
hf.spans[idx] = s;
return;
}
rcSpan* prev = 0;
rcSpan* cur = hf.spans[idx];
// Insert and merge spans.
while (cur)
{
if (cur->smin > s->smax)
{
// Current span is further than the new span, break.
break;
}
else if (cur->smax < s->smin)
{
// Current span is before the new span advance.
prev = cur;
cur = cur->next;
}
else
{
// Merge spans.
if (cur->smin < s->smin)
s->smin = cur->smin;
if (cur->smax > s->smax)
s->smax = cur->smax;
// Merge flags.
// if (s->smax == cur->smax)
if (rcAbs((int)s->smax - (int)cur->smax) <= 1)
s->flags |= cur->flags;
// Remove current span.
rcSpan* next = cur->next;
freeSpan(hf, cur);
if (prev)
prev->next = next;
else
hf.spans[idx] = next;
cur = next;
}
}
// Insert new span.
if (prev)
{
s->next = prev->next;
prev->next = s;
}
else
{
s->next = hf.spans[idx];
hf.spans[idx] = s;
}
}
static int clipPoly(const float* in, int n, float* out, float pnx, float pnz, float pd)
{
float d[12];
for (int i = 0; i < n; ++i)
d[i] = pnx*in[i*3+0] + pnz*in[i*3+2] + pd;
int m = 0;
for (int i = 0, j = n-1; i < n; j=i, ++i)
{
bool ina = d[j] >= 0;
bool inb = d[i] >= 0;
if (ina != inb)
{
float s = d[j] / (d[j] - d[i]);
out[m*3+0] = in[j*3+0] + (in[i*3+0] - in[j*3+0])*s;
out[m*3+1] = in[j*3+1] + (in[i*3+1] - in[j*3+1])*s;
out[m*3+2] = in[j*3+2] + (in[i*3+2] - in[j*3+2])*s;
m++;
}
if (inb)
{
out[m*3+0] = in[i*3+0];
out[m*3+1] = in[i*3+1];
out[m*3+2] = in[i*3+2];
m++;
}
}
return m;
}
static void rasterizeTri(const float* v0, const float* v1, const float* v2,
unsigned char flags, rcHeightfield& hf,
const float* bmin, const float* bmax,
const float cs, const float ics, const float ich)
{
const int w = hf.width;
const int h = hf.height;
float tmin[3], tmax[3];
const float by = bmax[1] - bmin[1];
// Calculate the bounding box of the triangle.
vcopy(tmin, v0);
vcopy(tmax, v0);
vmin(tmin, v1);
vmin(tmin, v2);
vmax(tmax, v1);
vmax(tmax, v2);
// If the triangle does not touch the bbox of the heightfield, skip the triagle.
if (!overlapBounds(bmin, bmax, tmin, tmax))
return;
// Calculate the footpring of the triangle on the grid.
int x0 = (int)((tmin[0] - bmin[0])*ics);
int y0 = (int)((tmin[2] - bmin[2])*ics);
int x1 = (int)((tmax[0] - bmin[0])*ics);
int y1 = (int)((tmax[2] - bmin[2])*ics);
x0 = rcClamp(x0, 0, w-1);
y0 = rcClamp(y0, 0, h-1);
x1 = rcClamp(x1, 0, w-1);
y1 = rcClamp(y1, 0, h-1);
// Clip the triangle into all grid cells it touches.
float in[7*3], out[7*3], inrow[7*3];
for (int y = y0; y <= y1; ++y)
{
// Clip polygon to row.
vcopy(&in[0], v0);
vcopy(&in[1*3], v1);
vcopy(&in[2*3], v2);
int nvrow = 3;
const float cz = bmin[2] + y*cs;
nvrow = clipPoly(in, nvrow, out, 0, 1, -cz);
if (nvrow < 3) continue;
nvrow = clipPoly(out, nvrow, inrow, 0, -1, cz+cs);
if (nvrow < 3) continue;
for (int x = x0; x <= x1; ++x)
{
// Clip polygon to column.
int nv = nvrow;
const float cx = bmin[0] + x*cs;
nv = clipPoly(inrow, nv, out, 1, 0, -cx);
if (nv < 3) continue;
nv = clipPoly(out, nv, in, -1, 0, cx+cs);
if (nv < 3) continue;
// Calculate min and max of the span.
float smin = in[1], smax = in[1];
for (int i = 1; i < nv; ++i)
{
smin = rcMin(smin, in[i*3+1]);
smax = rcMax(smax, in[i*3+1]);
}
smin -= bmin[1];
smax -= bmin[1];
// Skip the span if it is outside the heightfield bbox
if (smax < 0.0f) continue;
if (smin > by) continue;
// Clamp the span to the heightfield bbox.
if (smin < 0.0f) smin = bmin[1];
if (smax > by) smax = bmax[1];
// Snap the span to the heightfield height grid.
unsigned short ismin = (unsigned short)rcClamp((int)floorf(smin * ich), 0, 0x7fff);
unsigned short ismax = (unsigned short)rcClamp((int)ceilf(smax * ich), 0, 0x7fff);
addSpan(hf, x, y, ismin, ismax, flags);
}
}
}
void rcRasterizeTriangles(const float* bmin, const float* bmax, const float cs, const float ch,
const float* verts, int nv,
const int* tris, const unsigned char* flags, int nt,
rcHeightfield& solid)
{
rcTimeVal startTime = rcGetPerformanceTimer();
const float ics = 1.0f/cs;
const float ich = 1.0f/ch;
// Rasterize triangles.
for (int i = 0; i < nt; ++i)
{
const float* v0 = &verts[tris[i*3+0]*3];
const float* v1 = &verts[tris[i*3+1]*3];
const float* v2 = &verts[tris[i*3+2]*3];
// Rasterize.
rasterizeTri(v0, v1, v2, flags[i], solid, bmin, bmax, cs, ics, ich);
}
rcTimeVal endTime = rcGetPerformanceTimer();
if (rcGetLog())
rcGetLog()->log(RC_LOG_PROGRESS, "Rasterize: %.3f ms", rcGetDeltaTimeUsec(startTime, endTime)/1000.0f);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
#include "RecastTimer.h"
#ifdef WIN32
#include <windows.h>
rcTimeVal rcGetPerformanceTimer()
{
__int64 count;
QueryPerformanceCounter((LARGE_INTEGER*)&count);
return count;
}
int rcGetDeltaTimeUsec(rcTimeVal start, rcTimeVal end)
{
static __int64 freq = 0;
if (freq == 0)
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
__int64 elapsed = end - start;
return (int)(elapsed*1000000 / freq);
}
#else
#include <mach/mach_time.h>
rcTimeVal rcGetPerformanceTimer()
{
return mach_absolute_time();
}
int rcGetDeltaTimeUsec(rcTimeVal start, rcTimeVal end)
{
static mach_timebase_info_data_t timebaseInfo;
if (timebaseInfo.denom == 0)
mach_timebase_info(&timebaseInfo);
uint64_t elapsed = end - start;
uint64_t nanosec = elapsed * timebaseInfo.numer / timebaseInfo.denom;
return (int)(nanosec / 1000);
}
#endif